home *** CD-ROM | disk | FTP | other *** search
/ Meeting Pearls 4 / Meeting Pearls Vol. IV (1996)(GTI - Schatztruhe)[!].iso / Pearls / util / misc / CyberCron / Source / CyberCron.c < prev    next >
C/C++ Source or Header  |  1992-10-07  |  63KB  |  2,684 lines

  1.  /*
  2.   * CyberCron.c
  3.   * 
  4.   * Copyright © 1992 by Christopher A. Wichura (caw@miroc.chi.il.us). All rights
  5.   * reserved.
  6.   */
  7.  
  8. struct RxsLib *RexxSysBase;
  9. unsigned long ARexxLibCount = 0;
  10.  
  11.  /*
  12.   * here we have storage for the current crontab file, sendmail command and
  13.   * the name of our log file
  14.   */
  15.  
  16. UBYTE CronTabName[256];
  17. UBYTE SendMailCmd[256];
  18. UBYTE LogFile[256];
  19.  
  20.  /*
  21.   * storage for two large buffers.  we reuse these in several different
  22.   * places within CyberCron
  23.   */
  24.  
  25. UBYTE BigBufOne[BIG_BUF_SIZE];
  26. UBYTE BigBufTwo[BIG_BUF_SIZE];
  27.  
  28.  /*
  29.   * these are used by the ParseEvent() routine when no priority or stack size
  30.   * is specified.
  31.   */
  32.  
  33. ULONG DefaultStackSize = 4096;
  34. BYTE DefaultPriority = 0;
  35.  
  36.  /*
  37.   * this global is the list header for all cybernodes.  we tell who added the
  38.   * event (crontab or via a rexx command) by whether or not the CNB_CRONTAB
  39.   * bit is set in the cn_Flags field.
  40.   */
  41.  
  42. struct List EventList;
  43.  
  44. #define CYBERCRON GetString(&LocaleInfo, MSG_PROGNAME)
  45.  
  46. #define ARG_TEMPLATE "CRONTAB/K,LOGFILE/K,SENDMAIL/K,DEFSTACK/K/N,DEFPRI/K/N,TOOLPRI=CRONPRI/K/N,PORTNAME/K"
  47. enum CmdlineReadArgs {
  48.     ARG_CRONTAB,
  49.     ARG_LOGFILE,
  50.     ARG_SENDMAIL,
  51.     ARG_STACK,
  52.     ARG_PRI,
  53.     ARG_CPRI,
  54.     ARG_PORTNAME,
  55.     ARG_sizeof
  56. };
  57.  
  58.  /* extern references to our version and revision numbers */
  59.  
  60. extern ULONG __far Version;
  61. extern ULONG __far Revision;
  62. extern UBYTE __far VersionID[];
  63.  
  64.  /* storage for the pointer to StdErr */
  65.  
  66. BPTR StdErr = NULL;
  67.  
  68.  /* our old task priority */
  69.  
  70. WORD OldPriority = -1;
  71.  
  72.  /* for our main ReadArgs call so we can free it later */
  73.  
  74. struct RDArgs *MyArgs = NULL;
  75. struct RDArgs *ArgsPtr = NULL;
  76. STRPTR WBArgs = NULL;
  77.  
  78.  /* stuff used in launching/destroying jobs */
  79.  
  80. struct MyPublicSema *jobSema;
  81. ULONG NumSystemJobs = 0;
  82. ULONG NumARexxJobs = 0;
  83.  
  84.  /* Semaphore to protect Log() being called under EndSystemJob() */
  85.  
  86. struct MyPublicSema *logSema;
  87.  
  88.  /* stuff for our timer port */
  89.  
  90. struct MsgPort *TimerPort = NULL;
  91. struct timerequest TimerIO;
  92. BOOL TimerUP = FALSE;
  93. BOOL DoingTimeRequest = FALSE;
  94.  
  95.  /* stuff for our notify request */
  96.  
  97. struct NotifyRequest MyNotifyRequest;
  98. BYTE NotifySignal = -1;
  99. BOOL NotifyUP = FALSE;
  100.  
  101.  /* stuff for our rexx port */
  102.  
  103. struct MsgPort *RexxPort = NULL;
  104.  
  105.  /* global flags */
  106.  
  107. BOOL BringerDown = FALSE;    /* trying to quit ? */
  108. BOOL Suspended = FALSE;        /* currently suspended ? */
  109.  
  110.  /* storage for our old pr_WindowPtr */
  111.  
  112. APTR OldWindowPtr;
  113.  
  114.  /* specifies the maximum number of jobs for each of the queues */
  115.  
  116. struct JobQueue jobQueue[27];
  117.  
  118.  /*
  119.   * flag: should ErrorMsg() try and use requesters instead of writing to
  120.   * StdErr?
  121.   */
  122.  
  123. BOOL ErrorsToStdErr = TRUE;
  124.  
  125.  /* stuff for our locale support */
  126.  
  127. struct LocaleInfo LocaleInfo;
  128.  
  129.  /* storage for a couple library bases */
  130.  
  131. struct Library *UtilityBase = NULL;
  132. struct Library *OwnDevUnitBase = NULL;
  133.  
  134.  /* used in some date calculations */
  135.  
  136. static UBYTE DayTable[2][12] =
  137. {
  138.     {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
  139.     {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
  140. };
  141.  
  142.  /* this is our main routine */
  143.  
  144. int __regargs main(char *cmdptr, int cmdlen, struct WBStartup * WBMsg)
  145. {
  146.     char *ArgArray[ARG_sizeof];
  147.     ULONG NSignal, TSignal, RSignal;
  148.     ULONG signals;
  149.     ULONG numJobs;
  150.     int index;
  151.     BPTR lock;
  152.  
  153.     struct timeval tr_time;
  154.  
  155. #define TextBuf BigBufOne
  156.  
  157.     OldWindowPtr = ((struct Process *) FindTask(NULL))->pr_WindowPtr;
  158.     ((struct Process *) FindTask(NULL))->pr_WindowPtr = (APTR) - 1;
  159.  
  160.     StdErr = ((struct Process *) FindTask(NULL))->pr_CES;
  161.     if (StdErr == NULL)
  162.         StdErr = Output();
  163.  
  164.     /*
  165.      * the first thing we do is try and open up the local library and
  166.      * grab our catalog as we will need it if we want to print any error
  167.      * messages or other text
  168.      */
  169.  
  170.     if (LocaleInfo.li_LocaleBase = (APTR) OpenLibrary("locale.library", 38))
  171.         LocaleInfo.li_Catalog = OpenCatalogA(NULL, "CyberCron.catalog", NULL);
  172.  
  173.     /*
  174.      * if started from the Workbench then we need to make an argstring
  175.      * from our tooltypes as well as try and clone Workbench's CLI
  176.      * structure
  177.      */
  178.  
  179.     if (WBMsg) {
  180.         ErrorsToStdErr = FALSE;
  181.  
  182.         if (!(WBArgs = WBtoCLIargs(WBMsg, ARG_TEMPLATE)))
  183.             MyExit(5);
  184.  
  185.         /*
  186.          * make us into a CLI so we have a path to propigate to jobs
  187.          * we start
  188.          */
  189.         WB2CLI(WBMsg, DefaultStackSize, DOSBase);
  190.     }
  191.  
  192.     /* try and open OwnDevUnit.library */
  193.     OwnDevUnitBase = OpenLibrary(ODU_NAME, 0);
  194.  
  195.     /* try and open up utility.library */
  196.     if (!(UtilityBase = OpenLibrary("utility.library", 37))) {
  197.         ErrorMsg(MSG_COULDNT_OPEN, "utility.library");
  198.         MyExit(20);
  199.     }
  200.  
  201.     NewList(&EventList);
  202.  
  203.     if (!(jobSema = InitMyPublicSemaphore(GetString(&LocaleInfo, MSG_JOB_SEMA_NAME), sizeof(UBYTE) * JOB_TABLE_SIZE))) {
  204.         ErrorMsg(MSG_NO_PUB_SEMA);
  205.         MyExit(5);
  206.     }
  207.  
  208.     if (!(logSema = InitMyPublicSemaphore(GetString(&LocaleInfo, MSG_LOG_SEMA_NAME), 0))) {
  209.         ErrorMsg(MSG_NO_PUB_SEMA);
  210.         MyExit(5);
  211.     }
  212.  
  213.     /* do the stuff needed to call ReadArgs to parse the command line */
  214.     memset(ArgArray, 0, sizeof(ArgArray));
  215.  
  216.     if (!(MyArgs = (struct RDArgs *) AllocDosObject(DOS_RDARGS, TAG_DONE))) {
  217.         ErrorMsg(MSG_NO_RDARGS);
  218.         MyExit(5);
  219.     }
  220.  
  221.     if (!(MyArgs->RDA_ExtHelp = (UBYTE *) AllocVec(strlen(GetString(&LocaleInfo, MSG_ARG_HELP)) + strlen(GetString(&LocaleInfo, MSG_COPYRIGHT)) + (2 * strlen(CYBERCRON)) + strlen(VersionID) + 10, MEMF_CLEAR))) {
  222.         ErrorMsg(MSG_OUTOFMEM);
  223.         MyExit(5);
  224.     }
  225.  
  226.     sprintf((char *) MyArgs->RDA_ExtHelp, GetString(&LocaleInfo, MSG_ARG_HELP), CYBERCRON, VersionID, GetString(&LocaleInfo, MSG_COPYRIGHT), CYBERCRON);
  227.  
  228.     if (WBArgs) {
  229.         MyArgs->RDA_Source.CS_Buffer = WBArgs;
  230.         MyArgs->RDA_Source.CS_Length = strlen(WBArgs);
  231.         MyArgs->RDA_Source.CS_CurChr = 0L;
  232.     }
  233.  
  234.     /* now call ReadArgs to parse the command line */
  235.     ArgsPtr = ReadArgs(ARG_TEMPLATE, (LONG *) & ArgArray, MyArgs);
  236.  
  237.     /* free the memory we used for this ReadArgs() call */
  238.     FreeVec((char *) MyArgs->RDA_ExtHelp);
  239.     FreeVec(WBArgs);
  240.     WBArgs = NULL;
  241.  
  242.     if (!ArgsPtr) {
  243.         Fault(IoErr(), NULL, TextBuf, BIG_BUF_SIZE_BASE);
  244.         ErrorMsg(MSG_STRING_HACK, TextBuf);
  245.         MyExit(5);
  246.     }
  247.  
  248.     if (ArgArray[ARG_CRONTAB])
  249.         if (strlen(ArgArray[ARG_CRONTAB]) + 1 > sizeof(CronTabName)) {
  250.             ErrorMsg(MSG_CRONTAB_NAME_TOO_LONG);
  251.             MyExit(5);
  252.         }
  253.         else
  254.             strcpy(CronTabName, ArgArray[ARG_CRONTAB]);
  255.     else
  256.         strcpy(CronTabName, "S:CronTab");
  257.  
  258.     if (ArgArray[ARG_LOGFILE])
  259.         if (strlen(ArgArray[ARG_LOGFILE]) + 1 > sizeof(LogFile)) {
  260.             ErrorMsg(MSG_LOGFILE_NAME_TOO_LONG);
  261.             MyExit(5);
  262.         }
  263.         else
  264.             strcpy(LogFile, ArgArray[ARG_LOGFILE]);
  265.  
  266.     if (ArgArray[ARG_SENDMAIL])
  267.         if (strlen(ArgArray[ARG_SENDMAIL]) + 1 > sizeof(SendMailCmd)) {
  268.             ErrorMsg(MSG_SENDMAIL_COMMAND_TOO_LONG);
  269.             MyExit(5);
  270.         }
  271.         else
  272.             strcpy(SendMailCmd, ArgArray[ARG_SENDMAIL]);
  273.  
  274.     if (ArgArray[ARG_STACK])
  275.         DefaultStackSize = *((LONG *) ArgArray[ARG_STACK]);
  276.     else {
  277.  
  278.         /*
  279.          * if we have a cli attached to us then get the default stack
  280.          * size out of it.  Otherwise leave it be. WBtoCLIargs() will
  281.          * probably have set DefaultStackSize for us already in such
  282.          * a case.  If not, the hard-coded default of 4096 will be
  283.          * used
  284.          */
  285.  
  286.         struct CommandLineInterface *cli;
  287.  
  288.         if (cli = Cli())
  289.             DefaultStackSize = sizeof(LONG) * cli->cli_DefaultStack;
  290.     }
  291.  
  292.     if (DefaultStackSize < 2048)
  293.         DefaultStackSize = 2048;
  294.  
  295.     if (ArgArray[ARG_PRI])
  296.         DefaultPriority = *((LONG *) ArgArray[ARG_PRI]) & 0xFF;
  297.  
  298.     if (ArgArray[ARG_CPRI])
  299.         OldPriority = SetTaskPri(FindTask(NULL), *((LONG *) ArgArray[ARG_CPRI]) & 0xFF);
  300.  
  301.     /*
  302.      * open up our ARexx port.  Check to see if we're already using that
  303.      * port first, though.
  304.      */
  305.     Forbid();
  306.  
  307.     if (FindPort(ArgArray[ARG_PORTNAME] ? ArgArray[ARG_PORTNAME] : "CYBERCRON")) {
  308.         /* port already exists so fail */
  309.         Permit();
  310.         ErrorMsg(MSG_ALREADY_RUNNING, (ArgArray[ARG_PORTNAME] ? ArgArray[ARG_PORTNAME] : "CYBERCRON"));
  311.         MyExit(5);
  312.     }
  313.     else {
  314.         if (!(RexxPort = CreatePort((ArgArray[ARG_PORTNAME] ? ArgArray[ARG_PORTNAME] : "CYBERCRON"), 0))) {
  315.             Permit();
  316.             ErrorMsg(MSG_CANT_CREATE_AREXX_PORT);
  317.             MyExit(5);
  318.         }
  319.     }
  320.  
  321.     Permit();
  322.  
  323.     RSignal = 1L << RexxPort->mp_SigBit;
  324.  
  325.     /* open up the timer */
  326.     if (!(TimerPort = CreatePort(NULL, 0))) {
  327.         ErrorMsg(MSG_CANT_CREATE_TIMER_PORT);
  328.         MyExit(5);
  329.     }
  330.  
  331.     if (OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *) & TimerIO, 0)) {
  332.         ErrorMsg(MSG_COULDNT_OPEN, TIMERNAME);
  333.         MyExit(5);
  334.     }
  335.  
  336.     TimerIO.tr_node.io_Message.mn_ReplyPort = TimerPort;
  337.     TSignal = 1L << TimerPort->mp_SigBit;
  338.     TimerUP = TRUE;
  339.  
  340.     if ((NotifySignal = AllocSignal(-1)) == -1) {
  341.         ErrorMsg(MSG_COULDNT_ALLOC_NOTIFY_SIG);
  342.         MyExit(5);
  343.     }
  344.  
  345.     NSignal = 1L << NotifySignal;
  346.  
  347.     memset((char *) &MyNotifyRequest, 0, sizeof(struct NotifyRequest));
  348.  
  349.     MyNotifyRequest.nr_Name = CronTabName;
  350.     MyNotifyRequest.nr_Flags = NRF_SEND_SIGNAL;
  351.     MyNotifyRequest.nr_stuff.nr_Signal.nr_Task = FindTask(NULL);
  352.     MyNotifyRequest.nr_stuff.nr_Signal.nr_SignalNum = NotifySignal;
  353.  
  354.     if (StartNotify(&MyNotifyRequest) == DOSFALSE) {
  355.         ErrorMsg(MSG_COULDNT_START_NOTIFY);
  356.         MyExit(5);
  357.     }
  358.     NotifyUP = TRUE;
  359.  
  360.     ReadCronTab();
  361.  
  362.     /* initialize jobQueue Maximums */
  363.     for (index = 0; index < 27; index++) {
  364.         jobQueue[index].jq_Max = 1;
  365.         jobQueue[index].jq_Cur = 0;
  366.         NewList((struct List *) & jobQueue[index].jq_FIFOList);
  367.     }
  368.  
  369.     /*
  370.      * set queue 0 to 0xFFFFFFFF (infinite) so that jobs with no queue
  371.      * specified will use one with no limits
  372.      */
  373.     jobQueue[0].jq_Max = 0xFFFFFFFF;
  374.  
  375.     /* print a banner to the world saying we've started */
  376.     if (!WBMsg) {
  377.         sprintf(TextBuf, GetString(&LocaleInfo, MSG_DAEMON_STARTED), CYBERCRON, VersionID);
  378.         PutStr(TextBuf);
  379.         PutStr("\n");
  380.         PutStr(GetString(&LocaleInfo, MSG_COPYRIGHT));
  381.         Flush(Output());
  382.     }
  383.  
  384.     Log(MSG_DAEMON_STARTED, CYBERCRON, VersionID);
  385.     Log(MSG_PORTNAME, (ArgArray[ARG_PORTNAME] ? ArgArray[ARG_PORTNAME] : "CYBERCRON"));
  386.  
  387.     ErrorsToStdErr = TRUE;
  388.  
  389.     /*
  390.      * loop forever waiting for each minute and checking to see if we
  391.      * need to do anything.  also look for break, notify, etc.
  392.      * BringerDown can be set by the ARexx SHUTDOWN command so check it
  393.      * as well.
  394.      */
  395.  
  396.     for (; BringerDown == FALSE;) {
  397.         TimerIO.tr_node.io_Command = TR_ADDREQUEST;
  398.         TimerIO.tr_time.tv_micro = 0;
  399.         GetSysTime(&tr_time);
  400.         TimerIO.tr_time.tv_secs = 60 - tr_time.tv_secs % 60;
  401.         SetSignal(0L, TSignal);
  402.         SendIO((struct IORequest *) & TimerIO);
  403.         DoingTimeRequest = TRUE;
  404.  
  405.         signals = Wait(TSignal | NSignal | RSignal | SIGBREAKF_CTRL_C);
  406.  
  407.         if (signals & TSignal) {
  408.             GetMsg(TimerPort);
  409.             DoingTimeRequest = FALSE;
  410.             if (Suspended == FALSE)
  411.                 ScanForJobs();
  412.         }
  413.  
  414.         if (signals & NSignal)
  415.             if (lock = Lock(CronTabName, ACCESS_READ)) {
  416.                 FreeEvents(TRUE);
  417.  
  418.                 /*
  419.                  * not really an error, but ReadCronTab()
  420.                  * will send messages along the lines of
  421.                  * parse error in line # if they occur so we
  422.                  * spit this out to let them know why they
  423.                  * are getting these parse errors.
  424.                  */
  425.  
  426.                 ErrorMsg(MSG_CRONTAB_MODIFIED);
  427.                 Log(MSG_CRONTAB_MODIFIED);
  428.  
  429.                 ReadCronTab();
  430.                 UnLock(lock);
  431.             }
  432.  
  433.         if (signals & RSignal)
  434.             HandleRexxEvents();
  435.  
  436.         if (DoingTimeRequest) {
  437.             DoingTimeRequest = FALSE;
  438.  
  439.             AbortIO((struct IORequest *) & TimerIO);
  440.             WaitIO((struct IORequest *) & TimerIO);
  441.         }
  442.  
  443.         if (signals & SIGBREAKF_CTRL_C)
  444.             break;
  445.     }
  446.  
  447.     BringerDown = TRUE;
  448.  
  449.     ObtainSemaphore(&jobSema->mps_Sema);
  450.     numJobs = NumSystemJobs + NumARexxJobs;
  451.     ReleaseSemaphore(&jobSema->mps_Sema);
  452.  
  453.     if (numJobs) {
  454.         ErrorMsg(MSG_WAITING_TO_QUIT);
  455.  
  456.         for (; numJobs;) {
  457.             TimerIO.tr_node.io_Command = TR_ADDREQUEST;
  458.             TimerIO.tr_time.tv_secs = 15;
  459.             TimerIO.tr_time.tv_micro = 0;
  460.             SetSignal(0L, TSignal);
  461.             SendIO((struct IORequest *) & TimerIO);
  462.             DoingTimeRequest = TRUE;
  463.  
  464.             signals = Wait(TSignal | RSignal);
  465.  
  466.             if (signals & TSignal) {
  467.                 GetMsg(TimerPort);
  468.                 DoingTimeRequest = FALSE;
  469.             }
  470.  
  471.             if (signals & RSignal)
  472.                 HandleRexxEvents();
  473.  
  474.             if (DoingTimeRequest) {
  475.                 DoingTimeRequest = FALSE;
  476.  
  477.                 AbortIO((struct IORequest *) & TimerIO);
  478.                 WaitIO((struct IORequest *) & TimerIO);
  479.             }
  480.  
  481.             ObtainSemaphore(&jobSema->mps_Sema);
  482.             numJobs = NumSystemJobs + NumARexxJobs;
  483.             ReleaseSemaphore(&jobSema->mps_Sema);
  484.         }
  485.     }
  486.  
  487.     MyExit(0);
  488. }
  489.  
  490.  /* loop through the event list looking for any jobs to start */
  491.  
  492. void ScanForJobs(void)
  493. {
  494.     struct CyberNode *cn, *tcn;
  495.  
  496.     UBYTE DayOfWeek;
  497.     UWORD Month;
  498.     ULONG Day;
  499.     ULONG Hour;
  500.     ULONG Min;
  501.     BOOL TimeMatch;
  502.  
  503.     SystemTime_t st;
  504.     static ULONG LastScan = 0;
  505.  
  506.     GetSystemTime(&st, FALSE);
  507.  
  508.     /*
  509.      * figure out if the time has gone backwards.  This could happen if
  510.      * the clock was reloaded, etc.  We use a 5 minute threshhold.  If it
  511.      * goes back farther than that then we assume the user knows what
  512.      * they are doing.
  513.      */
  514.     {
  515.         ULONG MSSC = st.st_tvsecs / 60;    /* MSSC stands for Minutes
  516.                          * Since System Creation */
  517.  
  518.         if (MSSC >= (LastScan - 5) && MSSC <= LastScan)
  519.             return;
  520.         else
  521.             LastScan = MSSC;
  522.     }
  523.  
  524.     /* initilize the bit fields for us to do comparisons against */
  525.     DayOfWeek = 1L << st.st_DOW;
  526.     Month = 1L << st.st_Month;
  527.     Day = 1L << st.st_Day;
  528.     Hour = 1L << st.st_Hour;
  529.     if (st.st_Min > 31)
  530.         Min = 1L << (st.st_Min - 32);
  531.     else
  532.         Min = 1L << st.st_Min;
  533.  
  534.     /* loop through the list looking for events to do */
  535.     for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
  536.          cn = (struct CyberNode *) cn->cn_Node.ln_Succ) {
  537.  
  538.         TimeMatch = FALSE;
  539.  
  540.         if (cn->cn_DayOfWeek & DayOfWeek)
  541.             if (cn->cn_Month & Month)
  542.                 if (cn->cn_Day & Day)
  543.                     if (cn->cn_Hour & Hour)
  544.                         if ((st.st_Min > 31 && cn->cn_HiMin & Min) ||
  545.                             (st.st_Min < 32 && cn->cn_LoMin & Min))
  546.                             TimeMatch = TRUE;
  547.  
  548.         ObtainSemaphore(&jobSema->mps_Sema);
  549.  
  550.         if (TimeMatch || cn->cn_DelayedCount) {
  551.             if (ProcessQueue(cn, TimeMatch)) {
  552.                 if (cn->cn_Flags & CNF_REXX)
  553.                     StartRexxJob(cn);
  554.                 else
  555.                     StartSystemJob(cn);
  556.  
  557.                 if (cn->cn_Flags & CNF_EXECONE) {
  558.                     tcn = (struct CyberNode *) cn->cn_Node.ln_Pred;
  559.                     DeleteEvent(cn);
  560.                     cn = tcn;
  561.                 }
  562.             }
  563.         }
  564.  
  565.         ReleaseSemaphore(&jobSema->mps_Sema);
  566.     }
  567. }
  568.  
  569.  /*
  570.   * this routine will decide if we can run the job now or if it is to be
  571.   * delayed because we are currently at the maximum number of jobs allowed
  572.   * for the specified queue.  it serializes the delayed jobs on the queue by
  573.   * using a fifo mechanism which is maintained here
  574.   */
  575.  
  576. BOOL ProcessQueue(struct CyberNode * cn, BOOL TimeMatch)
  577. {
  578.     struct QueueFIFO *qf;
  579.     BOOL RetVal;
  580.     BOOL AddFIFO;
  581.  
  582.     AddFIFO = RetVal = FALSE;
  583.  
  584.     if (jobQueue[cn->cn_ObeyQueue].jq_Cur >= jobQueue[cn->cn_ObeyQueue].jq_Max) {
  585.         if (TimeMatch)
  586.             AddFIFO = TRUE;
  587.         else
  588.             return FALSE;
  589.     }
  590.  
  591.     if (!AddFIFO) {
  592.         if (cn->cn_DelayedCount) {
  593.             qf = (struct QueueFIFO *) RemHead((struct List *) & jobQueue[cn->cn_ObeyQueue].jq_FIFOList);
  594.  
  595.             if (qf->qf_CyberNode == cn) {
  596.                 RetVal = TRUE;
  597.                 cn->cn_DelayedCount--;
  598.                 FreeVec(qf);
  599.             }
  600.             else {
  601.                 AddHead((struct List *) & jobQueue[cn->cn_ObeyQueue].jq_FIFOList, (struct Node *) qf);
  602.             }
  603.  
  604.             /*
  605.              * if we were delayed but we would also start a job
  606.              * at this time anyway, we need to queue a new job to
  607.              * occur
  608.              */
  609.             if (TimeMatch)
  610.                 AddFIFO = TRUE;
  611.         }
  612.         else if (TimeMatch)
  613.             RetVal = TRUE;
  614.     }
  615.  
  616.     if (AddFIFO) {
  617.         if (qf = (struct QueueFIFO *) AllocVec(sizeof(struct QueueFIFO), MEMF_CLEAR)) {
  618.             qf->qf_CyberNode = cn;
  619.             AddTail((struct List *) & jobQueue[cn->cn_ObeyQueue].jq_FIFOList, (struct Node *) qf);
  620.             cn->cn_DelayedCount++;
  621.         }
  622.     }
  623.  
  624.     return RetVal;
  625. }
  626.  
  627.  
  628. void HandleRexxEvents(void)
  629. {
  630.     struct RexxMsg *msg;
  631.  
  632. /*    this is the table of ARexx commands that CyberCron knows.  the
  633.     format is as follows:
  634.  
  635.     1) a short that is the length of the command name
  636.     2) a pointer to the command name
  637.     3) a short to descibe the args to pass to the function.
  638.         value 0 = no args
  639.         value 1 = pointer to string after command name
  640.         value 2 = an integer
  641.         value 3 = pointer to the current ARexx message
  642.     4) a short to describe the return value from the function
  643.         value 0 = no returns, set rc to zero
  644.         value 1 = return an argstring
  645.         value 2 = return integer in rc
  646.         value 3 = return an argstring already in argstring format
  647.     5) a pointer to the function
  648. */
  649.  
  650. #define NUMRXCMDS 22
  651.     static struct {
  652.         short len;
  653.         char *RxCmd;
  654.         short args;
  655.         short ret;
  656.         APTR func;
  657.     } CmdTbl[NUMRXCMDS] = {
  658.  
  659.         /* indent makes this look so ugly...  sigh... */
  660.  
  661.         {
  662.             8, "SHUTDOWN", 0, 0, (APTR) & rx_Shutdown
  663.         },
  664.         {
  665.             4, "QUIT", 0, 0, (APTR) & rx_Shutdown
  666.         },
  667.         {
  668.             7, "VERSION", 0, 1, (APTR) & rx_Version
  669.         },
  670.         {
  671.             7, "SUSPEND", 0, 0, (APTR) & rx_Suspend
  672.         },
  673.         {
  674.             6, "RESUME", 0, 0, (APTR) & rx_Resume
  675.         },
  676.         {
  677.             14, "NEW_EVENT_FILE", 1, 2, (APTR) & rx_NewEventFile
  678.         },
  679.         {
  680.             16, "CLOSE_EVENT_FILE", 0, 0, (APTR) & rx_CloseEventFile
  681.         },
  682.         {
  683.             9, "ADD_EVENT", 1, 2, (APTR) & rx_AddEvent
  684.         },
  685.         {
  686.             11, "SHOW_STATUS", 0, 1, (APTR) & rx_ShowStatus
  687.         },
  688.         {
  689.             17, "PURGE_REXX_EVENTS", 0, 0, (APTR) & rx_PurgeRexxEvents
  690.         },
  691.         {
  692.             17, "DELETE_REXX_EVENT", 1, 2, (APTR) & rx_DeleteRexxEvent
  693.         },
  694.         {
  695.             12, "DELETE_EVENT", 1, 2, (APTR) & rx_DeleteEvent
  696.         },
  697.         {
  698.             11, "LIST_EVENTS", 0, 3, (APTR) & rx_ListEvents
  699.         },
  700.         {
  701.             10, "SHOW_EVENT", 1, 1, (APTR) & rx_ShowEvent
  702.         },
  703.         {
  704.             12, "NEW_LOG_FILE", 1, 2, (APTR) & rx_NewLogFile
  705.         },
  706.         {
  707.             14, "CLOSE_LOG_FILE", 0, 0, (APTR) & rx_CloseLogFile
  708.         },
  709.         {
  710.             13, "SET_QUEUE_MAX", 1, 2, (APTR) & rx_SetQueueMax
  711.         },
  712.         {
  713.             13, "GET_QUEUE_MAX", 1, 2, (APTR) & rx_GetQueueMax
  714.         },
  715.         {
  716.             15, "EVENT_NEXT_EXEC", 1, 1, (APTR) & rx_EventNextExec
  717.         },
  718.         {
  719.             18, "NEXT_EVENT_TO_EXEC", 0, 1, (APTR) & rx_NextEventToExec
  720.         },
  721.         {
  722.             11, "EXPAND_SSSC", 2, 1, (APTR) & rx_ExpandSSSC
  723.         },
  724.         {
  725.             13, "SSSC_TO_ASCII", 2, 1, (APTR) & rx_SSSCtoASCII
  726.         }
  727.     };
  728.  
  729.     /*
  730.      * if we can't get ahold of the rexx system library then we spin
  731.      * emptying our message port by replying to everything. shouldn't
  732.      * happen, but if some idiot tries sending us messages when they
  733.      * don't have ARexx then its better safe than sorry
  734.      */
  735.  
  736.     if (GetARexxLib() == FALSE) {
  737.         ErrorMsg(MSG_CANT_HANDLE_REXX_EVENT, RXSNAME);
  738.         Log(MSG_CANT_HANDLE_REXX_EVENT, RXSNAME);
  739.  
  740.         while ((msg = (struct RexxMsg *) GetMsg(RexxPort)))
  741.             ReplyMsg((struct Message *) msg);
  742.  
  743.         return;
  744.     }
  745.  
  746.     /*
  747.      * we've got the ARexx library so spin on our port looking for
  748.      * messages. if its a reply then a command/string we launched has
  749.      * finished and ARexx is returning its results to us.  Otherwise,
  750.      * it's a command we are to execute so call DoMsg() to dispatch it
  751.      */
  752.  
  753.     while ((msg = (struct RexxMsg *) GetMsg(RexxPort)))
  754.         if (msg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
  755.             if (!msg->rm_Args[3])
  756.                 Log(MSG_JOB_ENDED, (UWORD) msg->rm_Args[2]);
  757.  
  758.             /*
  759.              * when a system job can't find the command it wanted
  760.              * to execute, the shell automatically prints out
  761.              * that it couldn't find it or whatever the error
  762.              * might happen to be.  Thus, the user is informed of
  763.              * this based on wherever they may have redirected
  764.              * output.  ARexx, on the other hand, does not do
  765.              * this for us. So here I have duplicated what the RX
  766.              * activator prints out when it can't start a job.
  767.              * Note that ARexxErrorMsg() is a hack defined in
  768.              * CyberCron.h since this isn't normally available
  769.              * from the standard C= includes
  770.              */
  771.  
  772.             if (msg->rm_Result1)
  773.                 if (msg->rm_Result2)
  774.                     FPrintf(msg->rm_Stdout, GetString(&LocaleInfo, MSG_AREXX_RETURNED_2), msg->rm_Result1,
  775.                         msg->rm_Result2, (ARexxErrorMsg(msg->rm_Result2))->ns_Buff);
  776.                 else
  777.                     FPrintf(msg->rm_Stdout, GetString(&LocaleInfo, MSG_AREXX_RETURNED_1), msg->rm_Result1);
  778.  
  779.             Close(msg->rm_Stdout);
  780.             Close(msg->rm_Stdin);
  781.             FreeJobNum((UWORD) msg->rm_Args[2]);
  782.             ObtainSemaphore(&jobSema->mps_Sema);
  783.             jobQueue[(int) msg->rm_Args[4]].jq_Cur--;
  784.             ReleaseSemaphore(&jobSema->mps_Sema);
  785.             if (msg->rm_Args[1])
  786.                 DeleteArgstring(msg->rm_Args[1]);
  787.             DeleteArgstring(msg->rm_Args[0]);
  788.             DeleteRexxMsg(msg);
  789.             NumARexxJobs--;
  790.         }
  791.         else
  792.             DoMsg(msg, (APTR) & CmdTbl, NUMRXCMDS, BringerDown);
  793.  
  794.     FreeARexxLib();
  795. }
  796.  
  797. void rx_Shutdown(void)
  798. {
  799.     BringerDown = TRUE;
  800. }
  801.  
  802. STRPTR rx_Version(void)
  803. {
  804.     return VersionID;
  805. }
  806.  
  807. void rx_Suspend(void)
  808. {
  809.     Suspended = TRUE;
  810. }
  811.  
  812. void rx_Resume(void)
  813. {
  814.     Suspended = FALSE;
  815. }
  816.  
  817. int rx_NewEventFile(STRPTR name)
  818. {
  819.     BPTR lock;
  820.  
  821.     if (lock = Lock(name, ACCESS_READ)) {
  822.         if (strlen(name) + 1 > sizeof(CronTabName)) {
  823.             ErrorMsg(MSG_CRONTAB_NAME_TOO_LONG);
  824.             UnLock(lock);
  825.             return RC_WARN;
  826.         }
  827.  
  828.         FreeEvents(TRUE);
  829.  
  830.         if (NotifyUP) {
  831.             EndNotify(&MyNotifyRequest);
  832.             NotifyUP = FALSE;
  833.         }
  834.  
  835.         strcpy(CronTabName, name);
  836.  
  837.         memset((char *) &MyNotifyRequest, 0, sizeof(struct NotifyRequest));
  838.  
  839.         MyNotifyRequest.nr_Name = CronTabName;
  840.         MyNotifyRequest.nr_Flags = NRF_SEND_SIGNAL;
  841.         MyNotifyRequest.nr_stuff.nr_Signal.nr_Task = FindTask(NULL);
  842.         MyNotifyRequest.nr_stuff.nr_Signal.nr_SignalNum = NotifySignal;
  843.  
  844.         if (StartNotify(&MyNotifyRequest) == DOSFALSE) {
  845.             ErrorMsg(MSG_COULDNT_START_NOTIFY);
  846.             UnLock(lock);
  847.             strcpy(CronTabName, "<None>");
  848.             return RC_ERROR;
  849.         }
  850.         NotifyUP = TRUE;
  851.  
  852.         /* again, not really an error */
  853.         ErrorMsg(MSG_CRONTAB_CHANGED);
  854.         Log(MSG_CRONTAB_CHANGED);
  855.  
  856.         ReadCronTab();
  857.         UnLock(lock);
  858.         return RC_OK;
  859.     }
  860.  
  861.     return RC_WARN;
  862. }
  863.  
  864.  
  865. void rx_CloseEventFile(void)
  866. {
  867.     FreeEvents(TRUE);
  868.     EndNotify(&MyNotifyRequest);
  869.     NotifyUP = FALSE;
  870.     strcpy(CronTabName, "<None>");
  871. }
  872.  
  873. int rx_NewLogFile(STRPTR name)
  874. {
  875.     if (strlen(name) + 1 > sizeof(LogFile)) {
  876.         ErrorMsg(MSG_LOGFILE_NAME_TOO_LONG);
  877.         return RC_WARN;
  878.     }
  879.  
  880.     ObtainSemaphore(&logSema->mps_Sema);
  881.     strcpy(LogFile, name);
  882.     ReleaseSemaphore(&logSema->mps_Sema);
  883.  
  884.     return RC_OK;
  885. }
  886.  
  887. void rx_CloseLogFile(void)
  888. {
  889.     ObtainSemaphore(&logSema->mps_Sema);
  890.     LogFile[0] = '\0';
  891.     ReleaseSemaphore(&logSema->mps_Sema);
  892. }
  893.  
  894. STRPTR rx_ShowStatus(void)
  895. {
  896.     sprintf(BigBufOne, "%s \"%s\" \"%s\"",
  897.         (Suspended ? "SUSPENDED" : "ACTIVE"), CronTabName,
  898.         (LogFile[0] ? LogFile : (UBYTE *) "<None>"));
  899.  
  900.     return BigBufOne;
  901. }
  902.  
  903. void rx_PurgeRexxEvents(void)
  904. {
  905.     FreeEvents(FALSE);
  906. }
  907.  
  908. int rx_AddEvent(STRPTR event)
  909. {
  910.     struct CyberNode *cn;
  911.  
  912.     if (cn = ParseEvent(event)) {
  913.         AddTail(&EventList, (struct Node *) cn);
  914.         return RC_OK;
  915.     }
  916.     else
  917.         return RC_ERROR;
  918. }
  919.  
  920. int rx_DeleteRexxEvent(STRPTR name)
  921. {
  922.     struct CyberNode *cn;
  923.  
  924.     if ((cn = FindEvent(name)) && !(cn->cn_Flags & CNF_CRONTAB)) {
  925.         DeleteEvent(cn);
  926.         return RC_OK;
  927.     }
  928.  
  929.     return RC_ERROR;
  930. }
  931.  
  932. int rx_DeleteEvent(STRPTR name)
  933. {
  934.     struct CyberNode *cn;
  935.  
  936.     if (cn = FindEvent(name)) {
  937.         DeleteEvent(cn);
  938.         return RC_OK;
  939.     }
  940.  
  941.     return RC_ERROR;
  942. }
  943.  
  944. STRPTR rx_ListEvents(void)
  945. {
  946.     struct CyberNode *cn;
  947.     STRPTR string, string2;
  948.     ULONG num;
  949.  
  950.     num = 0;
  951.     for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
  952.          cn = (struct CyberNode *) cn->cn_Node.ln_Succ)
  953.         num++;
  954.  
  955.     if (num == 0)
  956.         return CreateArgstring("<None>", 6);
  957.  
  958.     if (!(string = CreateArgstring(NULL, num * 11)))
  959.         return NULL;
  960.  
  961.     string2 = string;
  962.     for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
  963.          cn = (struct CyberNode *) cn->cn_Node.ln_Succ) {
  964.         sprintf(string2, "0x%08lx ", cn);
  965.         string2 += 11;
  966.     }
  967.  
  968.     *--string2 = '\0';
  969.  
  970.     return string;
  971. }
  972.  
  973. #define rxSE_Buf BigBufOne
  974.  
  975. STRPTR rx_ShowEvent(STRPTR name)
  976. {
  977.     struct CyberNode *cn;
  978.     STRPTR ptr;
  979.     ULONG Bits[2];
  980.  
  981.     if (!(cn = FindEvent(name)))
  982.         return NULL;
  983.  
  984.     sprintf(rxSE_Buf, "0x%08lx ", cn);
  985.     ptr = &rxSE_Buf[11];
  986.  
  987.     if (cn->cn_Name) {
  988.         sprintf(ptr, ":NAME %s ", cn->cn_Name);
  989.         ptr += strlen(ptr);
  990.     }
  991.  
  992.     Bits[0] = cn->cn_LoMin, Bits[1] = cn->cn_HiMin;
  993.     UnParseBits(Bits, ptr, 0, 59);
  994.     ptr += strlen(ptr);
  995.     *ptr++ = ' ';
  996.  
  997.     Bits[1] = 0;
  998.  
  999.     Bits[0] = cn->cn_Hour;
  1000.     UnParseBits(Bits, ptr, 0, 23);
  1001.     ptr += strlen(ptr);
  1002.     *ptr++ = ' ';
  1003.  
  1004.     Bits[0] = cn->cn_Day;
  1005.     UnParseBits(Bits, ptr, 1, 31);
  1006.     ptr += strlen(ptr);
  1007.     *ptr++ = ' ';
  1008.  
  1009.     Bits[0] = cn->cn_Month;
  1010.     UnParseBits(Bits, ptr, 1, 12);
  1011.     ptr += strlen(ptr);
  1012.     *ptr++ = ' ';
  1013.  
  1014.     Bits[0] = cn->cn_DayOfWeek;
  1015.     UnParseBits(Bits, ptr, 0, 6);
  1016.     ptr += strlen(ptr);
  1017.     *ptr++ = ' ';
  1018.  
  1019.     if (cn->cn_Flags & CNF_EXECONE) {
  1020.         strcpy(ptr, ":EXECONCE ");
  1021.         ptr += 10;
  1022.     }
  1023.  
  1024.     if (cn->cn_Flags & CNF_NOLOG) {
  1025.         strcpy(ptr, ":NOLOG ");
  1026.         ptr += 7;
  1027.     }
  1028.  
  1029.     if (cn->cn_Flags & CNF_REXX) {
  1030.         strcpy(ptr, ":REXX ");
  1031.         ptr += 6;
  1032.     }
  1033.     else {
  1034.         if (cn->cn_Stack != DefaultStackSize) {
  1035.             sprintf(ptr, ":STACK %ld ", cn->cn_Stack);
  1036.             ptr += strlen(ptr);
  1037.         }
  1038.  
  1039.         if (cn->cn_Priority != DefaultPriority) {
  1040.             sprintf(ptr, ":PRI %ld ", cn->cn_Priority);
  1041.             ptr += strlen(ptr);
  1042.         }
  1043.  
  1044.         if (cn->cn_CustomShell) {
  1045.             sprintf(ptr, ":CUSTOMSH %s ", cn->cn_CustomShell);
  1046.             ptr += strlen(ptr);
  1047.         }
  1048.         else if (cn->cn_Flags & CNF_SYSSH) {
  1049.             strcpy(ptr, ":SYSSH ");
  1050.             ptr += 7;
  1051.         }
  1052.     }
  1053.  
  1054.     strcpy(ptr, cn->cn_Command);
  1055.     ptr += strlen(ptr);
  1056.     *ptr++ = ' ';
  1057.  
  1058.     if (cn->cn_Args) {
  1059.         strcpy(ptr, cn->cn_Args);
  1060.         ptr += strlen(ptr);
  1061.         *ptr++ = ' ';
  1062.     }
  1063.  
  1064.     if (cn->cn_ReDirIn) {
  1065.         sprintf(ptr, "< %s ", cn->cn_ReDirIn);
  1066.         ptr += strlen(ptr);
  1067.     }
  1068.  
  1069.     if (cn->cn_SendToUser && SendMailCmd[0]) {
  1070.         sprintf(ptr, ":MAILUSER %s ", cn->cn_SendToUser);
  1071.         ptr += strlen(ptr);
  1072.     }
  1073.     else {
  1074.         if (cn->cn_ReDirOut) {
  1075.             sprintf(ptr, "%s %s ", (cn->cn_Flags & CNF_OUTAPP ? ">>" : ">"), cn->cn_ReDirOut);
  1076.             ptr += strlen(ptr);
  1077.         }
  1078.     }
  1079.  
  1080.     if (cn->cn_ReDirErr) {
  1081.         sprintf(ptr, "2%s %s ", (cn->cn_Flags & CNF_ERRAPP ? ">>" : ">"), cn->cn_ReDirErr);
  1082.         ptr += strlen(ptr);
  1083.     }
  1084.  
  1085.     if (cn->cn_ObeyQueue) {
  1086.         sprintf(ptr, ":OBEYQUEUE %lc ", cn->cn_ObeyQueue + 'a' - 1);
  1087.         ptr += 13;
  1088.     }
  1089.  
  1090.     *--ptr = '\0';
  1091.  
  1092.     return rxSE_Buf;
  1093. }
  1094.  
  1095. int rx_SetQueueMax(STRPTR argline)
  1096. {
  1097.     int queueNum;
  1098.  
  1099.     if (isalpha(*argline)) {
  1100.         queueNum = tolower(*argline) - 'a' + 1;
  1101.  
  1102.         argline++;
  1103.  
  1104.         while (isspace(*argline++)) ;
  1105.  
  1106.         if (*--argline) {
  1107.             jobQueue[queueNum].jq_Max = atol(argline);
  1108.             return RC_OK;
  1109.         }
  1110.     }
  1111.  
  1112.     return RC_ERROR;
  1113. }
  1114.  
  1115. int rx_GetQueueMax(STRPTR argline)
  1116. {
  1117.     int queueNum;
  1118.  
  1119.     if (isalpha(*argline)) {
  1120.         queueNum = tolower(*argline) - 'a' + 1;
  1121.         return (int) jobQueue[queueNum].jq_Max;
  1122.     }
  1123.     else
  1124.         return -1;
  1125. }
  1126.  
  1127. STRPTR rx_EventNextExec(STRPTR name)
  1128. {
  1129.     struct CyberNode *cn;
  1130.     SystemTime_t st;
  1131.  
  1132.     if (!(cn = FindEvent(name)))
  1133.         return NULL;
  1134.  
  1135.     GetSystemTime(&st, FALSE);
  1136.  
  1137.     sprintf(BigBufOne, "%ld", EventNextExecutes(cn, &st));
  1138.  
  1139.     return BigBufOne;
  1140. }
  1141.  
  1142. STRPTR rx_NextEventToExec(void)
  1143. {
  1144.     struct CyberNode *cn, *bestcn;
  1145.     ULONG secs, bestsecs;
  1146.     SystemTime_t st;
  1147.  
  1148.     GetSystemTime(&st, FALSE);
  1149.  
  1150.     bestcn = (struct CyberNode *) NULL;
  1151.     bestsecs = -1;
  1152.  
  1153.     for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
  1154.          cn = (struct CyberNode *) cn->cn_Node.ln_Succ) {
  1155.         secs = EventNextExecutes(cn, &st);
  1156.         if (secs && secs < bestsecs) {
  1157.             bestcn = cn;
  1158.             bestsecs = secs;
  1159.         }
  1160.     }
  1161.  
  1162.     if (bestcn) {
  1163.         sprintf(BigBufOne, "0x%08lx %ld", bestcn, bestsecs);
  1164.         return BigBufOne;
  1165.     }
  1166.     else
  1167.         return "<None>";
  1168. }
  1169.  
  1170. STRPTR rx_ExpandSSSC(int SSSC)
  1171. {
  1172.     SystemTime_t st;
  1173.  
  1174.     st.st_tvsecs = SSSC;
  1175.  
  1176.     GetSystemTime(&st, TRUE);
  1177.  
  1178.     sprintf(BigBufOne, "%ld %ld %ld %ld %ld %ld %ld", st.st_Sec, st.st_Min,
  1179.         st.st_Hour, st.st_Day, st.st_Month, st.st_Year, st.st_DOW);
  1180.  
  1181.     return BigBufOne;
  1182. }
  1183.  
  1184. STRPTR rx_SSSCtoASCII(int SSSC)
  1185. {
  1186.     struct DateTime dat;
  1187.     UBYTE Day[LEN_DATSTRING + 1];
  1188.     UBYTE Date[LEN_DATSTRING + 1];
  1189.     UBYTE Time[LEN_DATSTRING + 1];
  1190.  
  1191.     dat.dat_Stamp.ds_Days = SSSC / SECSINDAY, SSSC %= SECSINDAY;
  1192.     dat.dat_Stamp.ds_Minute = SSSC / SECSINMINUTE, SSSC %= SECSINMINUTE;
  1193.     dat.dat_Stamp.ds_Tick = SSSC * TICKS_PER_SECOND;
  1194.  
  1195.     dat.dat_Format = FORMAT_DOS;
  1196.     dat.dat_Flags = 0;
  1197.     dat.dat_StrDay = Day;
  1198.     dat.dat_StrDate = Date;
  1199.     dat.dat_StrTime = Time;
  1200.  
  1201.     memset(Day, 0, LEN_DATSTRING + 1);
  1202.     memset(Date, 0, LEN_DATSTRING + 1);
  1203.     memset(Time, 0, LEN_DATSTRING + 1);
  1204.     DateToStr(&dat);
  1205.  
  1206.     sprintf(BigBufOne, "%s %s %s", Day, Date, Time);
  1207.  
  1208.     return BigBufOne;
  1209. }
  1210.  
  1211. STRPTR WBtoCLIargs(struct WBStartup * WBMsg, STRPTR ArgTemplate)
  1212. {
  1213.     UBYTE *Argline, *ArglineSave, *SourcePtr, *DestPtr;
  1214.     struct Library *IconBase;
  1215.     UBYTE tempChar;
  1216.     BOOL sawEqual;
  1217.     int index;
  1218.     ULONG size;
  1219.     BPTR oldDir;
  1220.     struct DiskObject *dob;
  1221.  
  1222.     if (!(IconBase = OpenLibrary("icon.library", 37)))
  1223.         return NULL;
  1224.  
  1225.     oldDir = CurrentDir(WBMsg->sm_ArgList[0].wa_Lock);
  1226.  
  1227.     if (!(dob = GetDiskObjectNew(WBMsg->sm_ArgList[0].wa_Name))) {
  1228.         CurrentDir(oldDir);
  1229.         CloseLibrary(IconBase);
  1230.         return NULL;
  1231.     }
  1232.  
  1233.     /*
  1234.      * if there are tooltypes then figure out how much memory we need to
  1235.      * allocate to hold them as a command line.  anything that isn't a
  1236.      * legal argument in our command line template is ignored.  This lets
  1237.      * things like DONOTWAIT be used without causing an error at
  1238.      * ReadArgs() time.
  1239.      */
  1240.  
  1241.     if (dob->do_ToolTypes)
  1242.         for (size = index = 0; dob->do_ToolTypes[index]; index++) {
  1243.             SourcePtr = dob->do_ToolTypes[index];
  1244.             DestPtr = BigBufOne;
  1245.  
  1246.             while (tempChar = *SourcePtr++)
  1247.                 if (tempChar == '=')
  1248.                     break;
  1249.                 else
  1250.                     *DestPtr++ = tempChar;
  1251.  
  1252.             *DestPtr = '\0';
  1253.  
  1254.             if (FindArg(ArgTemplate, BigBufOne) != -1)
  1255.                 size += strlen(dob->do_ToolTypes[index]) + 3;
  1256.         }
  1257.     else
  1258.         size = 0;
  1259.  
  1260.     if (Argline = AllocVec(size + 2, MEMF_CLEAR)) {
  1261.         ArglineSave = Argline;
  1262.  
  1263.         if (dob->do_ToolTypes)
  1264.             for (index = 0; dob->do_ToolTypes[index]; index++) {
  1265.                 SourcePtr = dob->do_ToolTypes[index];
  1266.                 DestPtr = BigBufOne;
  1267.                 sawEqual = FALSE;
  1268.  
  1269.                 while (tempChar = *SourcePtr++)
  1270.                     if (tempChar == '=') {
  1271.                         sawEqual = TRUE;
  1272.                         break;
  1273.                     }
  1274.                     else
  1275.                         *DestPtr++ = tempChar;
  1276.  
  1277.                 *DestPtr = '\0';
  1278.  
  1279.                 if (FindArg(ArgTemplate, BigBufOne) != -1) {
  1280.                     CopyMem(BigBufOne, Argline, DestPtr - BigBufOne);
  1281.                     Argline += DestPtr - BigBufOne;
  1282.  
  1283.                     /*
  1284.                      * if we saw an equals sign when we
  1285.                      * broke the argument name out then
  1286.                      * we know we need to copy the
  1287.                      * argument's data over.  if we
  1288.                      * didn't see it then this argument
  1289.                      * is a switch and thus has no more
  1290.                      * data to copy over
  1291.                      */
  1292.  
  1293.                     if (sawEqual) {
  1294.                         *Argline++ = ' ';
  1295.                         *Argline++ = '\"';
  1296.  
  1297.                         while (tempChar = *SourcePtr++)
  1298.                             *Argline++ = tempChar;
  1299.  
  1300.                         *Argline++ = '\"';
  1301.                     }
  1302.  
  1303.                     *Argline++ = ' ';
  1304.                 }
  1305.             }
  1306.  
  1307.         *Argline++ = '\n';
  1308.         *Argline = '\0';
  1309.     }
  1310.     else
  1311.         ArglineSave = NULL;
  1312.  
  1313.     if (dob->do_StackSize)
  1314.         DefaultStackSize = dob->do_StackSize;
  1315.  
  1316.     FreeDiskObject(dob);
  1317.     CurrentDir(oldDir);
  1318.     CloseLibrary(IconBase);
  1319.     return ArglineSave;
  1320. }
  1321.  
  1322. void MyExit(int error)
  1323. {
  1324.     if (OldPriority != -1)
  1325.         SetTaskPri(FindTask(NULL), OldPriority);
  1326.  
  1327.     if (ARexxLibCount)
  1328.         CloseLibrary((struct Library *) RexxSysBase);
  1329.  
  1330.     FreeEvents(TRUE);
  1331.     FreeEvents(FALSE);
  1332.  
  1333.     if (NotifyUP)
  1334.         EndNotify(&MyNotifyRequest);
  1335.  
  1336.     FreeSignal(NotifySignal);
  1337.  
  1338.     if (TimerUP) {
  1339.         if (DoingTimeRequest) {
  1340.             AbortIO((struct IORequest *) & TimerIO);
  1341.             WaitIO((struct IORequest *) & TimerIO);
  1342.         }
  1343.  
  1344.         CloseDevice((struct IORequest *) & TimerIO);
  1345.     }
  1346.  
  1347.     if (TimerPort)
  1348.         DeletePort(TimerPort);
  1349.  
  1350.     if (RexxPort)
  1351.         DeletePort(RexxPort);
  1352.  
  1353.     if (ArgsPtr)
  1354.         FreeArgs(ArgsPtr);
  1355.  
  1356.     if (MyArgs)
  1357.         FreeDosObject(DOS_RDARGS, MyArgs);
  1358.  
  1359.     if (logSema)
  1360.         FreeMyPublicSemaphore(logSema);
  1361.  
  1362.     if (jobSema)
  1363.         FreeMyPublicSemaphore(jobSema);
  1364.  
  1365.     CloseLibrary(UtilityBase);
  1366.  
  1367.     CloseLibrary(OwnDevUnitBase);
  1368.  
  1369.     FreeVec(WBArgs);
  1370.  
  1371.     if (LocaleInfo.li_LocaleBase) {
  1372.         CloseCatalog(LocaleInfo.li_Catalog);
  1373.         CloseLibrary((struct Library *) LocaleInfo.li_LocaleBase);
  1374.     }
  1375.  
  1376.     ((struct Process *) FindTask(NULL))->pr_WindowPtr = OldWindowPtr;
  1377.  
  1378.     XCEXIT(error);
  1379. }
  1380.  
  1381.  /*
  1382.   * this routine will read the crontab file, calling ParseEvent() to create
  1383.   * CyberNodes, and then link them into the event list.
  1384.   * 
  1385.   */
  1386.  
  1387. #define RCT_Buf BigBufOne
  1388.  
  1389. void ReadCronTab(void)
  1390. {
  1391.     BPTR fh;
  1392.     struct CyberNode *cn;
  1393.     ULONG line = 0;
  1394.     LONG error;
  1395.  
  1396.     if (!(fh = Open(CronTabName, MODE_OLDFILE))) {
  1397.         ErrorMsg(MSG_OPENING_CRONTAB, CronTabName);
  1398.         Log(MSG_OPENING_CRONTAB, CronTabName);
  1399.         return;
  1400.     }
  1401.  
  1402.     while (FGets(fh, RCT_Buf, BIG_BUF_SIZE_BASE)) {
  1403.         line++;
  1404.  
  1405.         if (RCT_Buf[0] == '#' || RCT_Buf[0] == '\n')
  1406.             continue;
  1407.  
  1408.         if (cn = ParseEvent(RCT_Buf)) {
  1409.             cn->cn_Flags |= CNF_CRONTAB;
  1410.             AddTail(&EventList, (struct Node *) cn);
  1411.         }
  1412.         else {
  1413.             ErrorMsg(MSG_PARSING, line, CronTabName);
  1414.             Log(MSG_PARSING, line, CronTabName);
  1415.         }
  1416.     }
  1417.  
  1418.     error = IoErr();
  1419.  
  1420.     if (error) {
  1421.         Fault(error, NULL, RCT_Buf, BIG_BUF_SIZE_BASE);
  1422.         ErrorMsg(MSG_IO_FAULT_IN_CRONTAB, error, RCT_Buf, line + 1, CronTabName);
  1423.         Log(MSG_IO_FAULT_IN_CRONTAB, error, RCT_Buf, line + 1, CronTabName);
  1424.     }
  1425.  
  1426.     Close(fh);
  1427. }
  1428.  
  1429.  /*
  1430.   * this routine will parse an ASCII string and make a CyberNode out of it.
  1431.   * it returns NULL if there was an error during the parse.  otherwise it
  1432.   * returns a pointer to the node.
  1433.   * 
  1434.   * note that we do something really sneaky here.  we use the ReadArgs() routine
  1435.   * to do the initial parse!.  This means that the order in which items occur
  1436.   * in a crontab entry can be virtually anything the user desires!
  1437.   */
  1438.  
  1439. #define PE_TEMPLATE "Event/M,</K,>/K,>>/K,2>/K,2>>/K,:NAME/K,:STACK/K/N,:PRI/K/N,:CUSTOMSH/K,:MAILUSER/K,:OBEYQUEUE/K,:SYSSH/S,:REXX/S,:NOLOG/S,:EXECONCE/S"
  1440.  
  1441. enum ParseEventReadArgs {
  1442.     PEARG_EVENT,
  1443.     PEARG_REDIRIN,
  1444.     PEARG_REDIROUT,
  1445.     PEARG_REDIROUT2,
  1446.     PEARG_REDIRERR,
  1447.     PEARG_REDIRERR2,
  1448.     PEARG_REXXNAME,
  1449.     PEARG_STACK,
  1450.     PEARG_PRI,
  1451.     PEARG_CUSTOMSH,
  1452.     PEARG_MAILUSER,
  1453.     PEARG_OBEYQUEUE,
  1454.     PEARG_SYSSH,
  1455.     PEARG_REXX,
  1456.     PEARG_NOLOG,
  1457.     PEARG_EXECONE,
  1458.     PEARG_sizeof
  1459. };
  1460.  
  1461. enum Events {
  1462.     EVENT_MINUTE,
  1463.     EVENT_HOUR,
  1464.     EVENT_DAY,
  1465.     EVENT_MONTH,
  1466.     EVENT_DOW,
  1467.     EVENT_COMMAND,
  1468.     EVENT_ARGS
  1469. };
  1470.  
  1471. #define PE_Buf BigBufTwo
  1472.  
  1473. struct CyberNode *ParseEvent(STRPTR event)
  1474. {
  1475.     struct CyberNode *cn;
  1476.     struct RDArgs *PArgsPtr;
  1477.     struct RDArgs *PMyArgs;
  1478.     char *ArgArray[PEARG_sizeof];
  1479.  
  1480.     register char **EventArgs;
  1481.     register int index;
  1482.     ULONG size;
  1483.     ULONG Bits[2];
  1484.  
  1485.     /* allocate our RDArgs structure */
  1486.     if (!(PMyArgs = (struct RDArgs *) AllocDosObject(DOS_RDARGS, TAG_DONE)))
  1487.         return (struct CyberNode *) NULL;
  1488.  
  1489.     PMyArgs->RDA_Flags |= RDAF_NOPROMPT;
  1490.  
  1491.     /*
  1492.      * set up the buffer for our ReadArgs() call.  We have to copy over
  1493.      * the string and put a new line at the end of it because of a
  1494.      * limitation of ReadArgs().  sigh.
  1495.      */
  1496.  
  1497.     {
  1498.         ULONG length;
  1499.  
  1500.         length = strlen(event);
  1501.         if (length + 2 > sizeof(PE_Buf)) {
  1502.             FreeDosObject(DOS_RDARGS, PMyArgs);
  1503.             return (struct CyberNode *) NULL;
  1504.         }
  1505.  
  1506.         CopyMem(event, PE_Buf, length);
  1507.  
  1508.         PE_Buf[length++] = '\n';
  1509.         PE_Buf[length] = '\0';
  1510.  
  1511.         PMyArgs->RDA_Source.CS_Buffer = PE_Buf;
  1512.         PMyArgs->RDA_Source.CS_Length = length;
  1513.         PMyArgs->RDA_Source.CS_CurChr = 0L;
  1514.     }
  1515.  
  1516.     /*
  1517.      * here we walk through the event line to make sure it isnt all
  1518.      * blank.
  1519.      */
  1520.  
  1521.     while (isspace(*event))
  1522.         event++;
  1523.  
  1524.     if (!*event) {
  1525.         FreeDosObject(DOS_RDARGS, PMyArgs);
  1526.         return (struct CyberNode *) NULL;
  1527.     }
  1528.  
  1529.     memset(ArgArray, 0, sizeof(ArgArray));
  1530.  
  1531.     /* now we call ReadArgs() */
  1532.     PArgsPtr = ReadArgs(PE_TEMPLATE, (LONG *) & ArgArray, PMyArgs);
  1533.  
  1534.     if (!PArgsPtr) {
  1535.         FreeDosObject(DOS_RDARGS, PMyArgs);
  1536.         return (struct CyberNode *) NULL;
  1537.     }
  1538.  
  1539.     /*
  1540.      * if they specified a name to be known as via the rexx port, make
  1541.      * sure it doesn't start with 0x because that's what we use to prefix
  1542.      * a hex number for nodes with no name and we don't want the user
  1543.      * fooling around with names we consider private.
  1544.      */
  1545.  
  1546.     if (ArgArray[PEARG_REXXNAME])
  1547.         if (ArgArray[PEARG_REXXNAME][0] == '0' && tolower(ArgArray[PEARG_REXXNAME][1]) == 'x') {
  1548.             FreeArgs(PArgsPtr);
  1549.             FreeDosObject(DOS_RDARGS, PMyArgs);
  1550.             return (struct CyberNode *) NULL;
  1551.         }
  1552.  
  1553.  
  1554.     /*
  1555.      * ok, ReadArgs has parsed the event for us.  make sure that we have
  1556.      * at least 5 time specs and a command name.
  1557.      */
  1558.     EventArgs = (char **) ArgArray[PEARG_EVENT];
  1559.  
  1560.     for (index = EVENT_MINUTE; index < EVENT_COMMAND; index++, EventArgs++)
  1561.         if (!*EventArgs || !isdigit(*EventArgs[0]) && *EventArgs[0] != '*') {
  1562.             FreeArgs(PArgsPtr);
  1563.             FreeDosObject(DOS_RDARGS, PMyArgs);
  1564.             return (struct CyberNode *) NULL;
  1565.         }
  1566.  
  1567.     /*
  1568.      * we have the five time spec strings.  now check to make sure we
  1569.      * have a command name.  we will also calculate its size as well as
  1570.      * the size of any args for the command while we are at it
  1571.      */
  1572.  
  1573.     if (!*EventArgs) {
  1574.         FreeArgs(PArgsPtr);
  1575.         FreeDosObject(DOS_RDARGS, PMyArgs);
  1576.         return (struct CyberNode *) NULL;
  1577.     }
  1578.  
  1579.     size = strlen(*EventArgs++) + 1;
  1580.  
  1581.     while (*EventArgs)
  1582.         size += strlen(*EventArgs++) + 1;
  1583.  
  1584.     /*
  1585.      * now figure out the memory needed to store the other textual items
  1586.      * for this node
  1587.      */
  1588.  
  1589.     if (ArgArray[PEARG_REDIRIN])
  1590.         size += strlen(ArgArray[PEARG_REDIRIN]) + 1;
  1591.  
  1592.     if (ArgArray[PEARG_REDIROUT])
  1593.         size += strlen(ArgArray[PEARG_REDIROUT]) + 1;
  1594.     if (ArgArray[PEARG_REDIROUT2]) {
  1595.         size += strlen(ArgArray[PEARG_REDIROUT2]) + 1;
  1596.         if (ArgArray[PEARG_REDIROUT])
  1597.             size -= strlen(ArgArray[PEARG_REDIROUT]) + 1;
  1598.     }
  1599.  
  1600.     if (ArgArray[PEARG_REDIRERR])
  1601.         size += strlen(ArgArray[PEARG_REDIRERR]) + 1;
  1602.     if (ArgArray[PEARG_REDIRERR2]) {
  1603.         size += strlen(ArgArray[PEARG_REDIRERR2]) + 1;
  1604.         if (ArgArray[PEARG_REDIRERR])
  1605.             size -= strlen(ArgArray[PEARG_REDIRERR]) + 1;
  1606.     }
  1607.  
  1608.     if (ArgArray[PEARG_REXXNAME])
  1609.         size += strlen(ArgArray[PEARG_REXXNAME]) + 1;
  1610.     if (ArgArray[PEARG_CUSTOMSH])
  1611.         size += strlen(ArgArray[PEARG_CUSTOMSH]) + 1;
  1612.  
  1613.     if (ArgArray[PEARG_MAILUSER])
  1614.         size += strlen(ArgArray[PEARG_MAILUSER]) + 1;
  1615.  
  1616.     if (ArgArray[PEARG_OBEYQUEUE] && !isalpha(ArgArray[PEARG_OBEYQUEUE][0])) {
  1617.         FreeArgs(PArgsPtr);
  1618.         FreeDosObject(DOS_RDARGS, PMyArgs);
  1619.         return (struct CyberNode *) NULL;
  1620.     }
  1621.  
  1622.     if (!(cn = (struct CyberNode *) AllocVec(size + sizeof(struct CyberNode) + 1, MEMF_CLEAR))) {
  1623.         FreeArgs(PArgsPtr);
  1624.         FreeDosObject(DOS_RDARGS, PMyArgs);
  1625.         return (struct CyberNode *) NULL;
  1626.     }
  1627.  
  1628.     /*
  1629.      * now that we have got the memory for the CyberNode start filling it
  1630.      * in.  we start by testing the STACK and PRI fields of the arg list
  1631.      * and use Atol() to get their values if present.  We then test the
  1632.      * REXX and NOLOG flags and use them to set the cn_Flags element.
  1633.      */
  1634.  
  1635.     if (ArgArray[PEARG_STACK])
  1636.         cn->cn_Stack = *((LONG *) ArgArray[PEARG_STACK]);
  1637.     if (cn->cn_Stack < 2048)
  1638.         cn->cn_Stack = DefaultStackSize;
  1639.  
  1640.     if (ArgArray[PEARG_PRI])
  1641.         cn->cn_Priority = *((LONG *) ArgArray[PEARG_PRI]) & 0xFF;
  1642.     else
  1643.         cn->cn_Priority = DefaultPriority;
  1644.  
  1645.     if (ArgArray[PEARG_OBEYQUEUE])
  1646.         cn->cn_ObeyQueue = tolower(ArgArray[PEARG_OBEYQUEUE][0]) - 'a' + 1;
  1647.  
  1648.     if (ArgArray[PEARG_REXX])
  1649.         cn->cn_Flags |= CNF_REXX;
  1650.  
  1651.     if (ArgArray[PEARG_NOLOG])
  1652.         cn->cn_Flags |= CNF_NOLOG;
  1653.  
  1654.     if (ArgArray[PEARG_SYSSH])
  1655.         cn->cn_Flags |= CNF_SYSSH;
  1656.  
  1657.     if (ArgArray[PEARG_EXECONE])
  1658.         cn->cn_Flags |= CNF_EXECONE;
  1659.  
  1660.     /*
  1661.      * now prepare to copy the textual items over into memory behind the
  1662.      * CyberNode
  1663.      */
  1664.  
  1665.     event = (char *) cn + sizeof(struct CyberNode);
  1666.  
  1667.     if (ArgArray[PEARG_REXXNAME]) {
  1668.         cn->cn_Name = event;
  1669.         size = strlen(ArgArray[PEARG_REXXNAME]) + 1;
  1670.         CopyMem(ArgArray[PEARG_REXXNAME], event, size);
  1671.         event += size;
  1672.     }
  1673.     if (ArgArray[PEARG_REDIRIN]) {
  1674.         cn->cn_ReDirIn = event;
  1675.         size = strlen(ArgArray[PEARG_REDIRIN]) + 1;
  1676.         CopyMem(ArgArray[PEARG_REDIRIN], event, size);
  1677.         event += size;
  1678.     }
  1679.     if (ArgArray[PEARG_REDIROUT] && !ArgArray[PEARG_REDIROUT2]) {
  1680.         cn->cn_ReDirOut = event;
  1681.         size = strlen(ArgArray[PEARG_REDIROUT]) + 1;
  1682.         CopyMem(ArgArray[PEARG_REDIROUT], event, size);
  1683.         event += size;
  1684.     }
  1685.     if (ArgArray[PEARG_REDIROUT2]) {
  1686.         cn->cn_ReDirOut = event;
  1687.         size = strlen(ArgArray[PEARG_REDIROUT2]) + 1;
  1688.         CopyMem(ArgArray[PEARG_REDIROUT2], event, size);
  1689.         event += size;
  1690.         cn->cn_Flags |= CNF_OUTAPP;
  1691.     }
  1692.     if (ArgArray[PEARG_REDIRERR] && !ArgArray[PEARG_REDIRERR2]) {
  1693.         cn->cn_ReDirErr = event;
  1694.         size = strlen(ArgArray[PEARG_REDIRERR]) + 1;
  1695.         CopyMem(ArgArray[PEARG_REDIRERR], event, size);
  1696.         event += size;
  1697.     }
  1698.     if (ArgArray[PEARG_REDIRERR2]) {
  1699.         cn->cn_ReDirErr = event;
  1700.         size = strlen(ArgArray[PEARG_REDIRERR2]) + 1;
  1701.         CopyMem(ArgArray[PEARG_REDIRERR2], event, size);
  1702.         event += size;
  1703.         cn->cn_Flags |= CNF_ERRAPP;
  1704.     }
  1705.     if (ArgArray[PEARG_CUSTOMSH]) {
  1706.         cn->cn_CustomShell = event;
  1707.         size = strlen(ArgArray[PEARG_CUSTOMSH]) + 1;
  1708.         CopyMem(ArgArray[PEARG_CUSTOMSH], event, size);
  1709.         event += size;
  1710.     }
  1711.     if (ArgArray[PEARG_MAILUSER]) {
  1712.         cn->cn_SendToUser = event;
  1713.         size = strlen(ArgArray[PEARG_MAILUSER]) + 1;
  1714.         CopyMem(ArgArray[PEARG_MAILUSER], event, size);
  1715.         event += size;
  1716.     }
  1717.  
  1718.     EventArgs = (char **) ArgArray[PEARG_EVENT];
  1719.     cn->cn_Command = event;
  1720.     index = EVENT_COMMAND;
  1721.     size = strlen(EventArgs[index]);
  1722.     CopyMem(EventArgs[index++], event, size);
  1723.     event += size;
  1724.     *event++ = 0;
  1725.  
  1726.     if (EventArgs[index]) {
  1727.         cn->cn_Args = event;
  1728.         while (EventArgs[index]) {
  1729.             size = strlen(EventArgs[index]);
  1730.             CopyMem(EventArgs[index++], event, size);
  1731.             event += size;
  1732.             *event++ = ' ';
  1733.         }
  1734.     }
  1735.     else
  1736.         cn->cn_Args = NULL;
  1737.  
  1738.     *--event = 0;
  1739.  
  1740.     /*
  1741.      * Now we need to convert the ASCII time values into bitmaps to store
  1742.      * in the node.  Note that we do not check to see if the time strings
  1743.      * are within range or not.  We simply logically AND away any invalid
  1744.      * bits and use whats left.
  1745.      */
  1746.  
  1747.     ParseBits(Bits, EventArgs[EVENT_MINUTE]);
  1748.     cn->cn_LoMin = Bits[0], cn->cn_HiMin = Bits[1] & 0xFFFFFFF;
  1749.  
  1750.     ParseBits(Bits, EventArgs[EVENT_HOUR]);
  1751.     cn->cn_Hour = Bits[0] & 0xFFFFFF;
  1752.  
  1753.     ParseBits(Bits, EventArgs[EVENT_DAY]);
  1754.     cn->cn_Day = Bits[0] & 0xFFFFFFFE;
  1755.  
  1756.     ParseBits(Bits, EventArgs[EVENT_MONTH]);
  1757.     cn->cn_Month = Bits[0] & 0x1FFE;
  1758.  
  1759.     ParseBits(Bits, EventArgs[EVENT_DOW]);
  1760.     cn->cn_DayOfWeek = Bits[0] & 0x7F;
  1761.  
  1762.     /*
  1763.      * we need to see if cn_Command has any spaces in it and set
  1764.      * cn_CommandHasSpace accordingly.  this lets the job starters make
  1765.      * some more intelligent choices.
  1766.      */
  1767.  
  1768.     {
  1769.         BOOL HasSpace;
  1770.         STRPTR ptr;
  1771.  
  1772.         HasSpace = FALSE;
  1773.  
  1774.         for (ptr = cn->cn_Command; *ptr; ptr++)
  1775.             if (isspace(*ptr)) {
  1776.                 HasSpace = TRUE;
  1777.                 break;
  1778.             }
  1779.  
  1780.         cn->cn_CommandHasSpace = HasSpace;
  1781.     }
  1782.  
  1783.     FreeArgs(PArgsPtr);
  1784.     FreeDosObject(DOS_RDARGS, PMyArgs);
  1785.     return cn;
  1786. }
  1787.  
  1788.  /*
  1789.   * this routine will try to figure out the next time an event would be
  1790.   * executed.
  1791.   */
  1792.  
  1793. ULONG EventNextExecutes(struct CyberNode * cn, SystemTime_t * st)
  1794. {
  1795.     ULONG days, days2;
  1796.     ULONG min, hour, day, month, dow, year;
  1797.     ULONG dayTarget, yearTarget;
  1798.     BOOL isLeap;
  1799.  
  1800.     for (days = -1, year = 1978; year < st->st_Year; year++)
  1801.         days += LEAP(year) ? 366 : 365;
  1802.  
  1803.     yearTarget = year + 8;
  1804.  
  1805.     for (; year < yearTarget; year++) {
  1806.         isLeap = LEAP(year);
  1807.  
  1808.         for (month = (year == st->st_Year) ? st->st_Month : 1; month < 13; month++)
  1809.             if (cn->cn_Month & (1L << month)) {
  1810.                 day = (month == st->st_Month && year == st->st_Year) ? st->st_Day : 1;
  1811.                 dayTarget = DayTable[isLeap][month - 1] + 1;
  1812.  
  1813.                 {
  1814.                     int i;
  1815.  
  1816.                     for (days2 = days, i = 1; i < month; i++)
  1817.                         days2 += DayTable[isLeap][i - 1];
  1818.                 }
  1819.  
  1820.                 dow = (days2 + day) % 7;
  1821.  
  1822.                 for (; day < dayTarget; day++, dow = ++dow % 7)
  1823.                     if ((cn->cn_DayOfWeek & (1L << dow)) && (cn->cn_Day & (1L << day)))
  1824.                         for (hour = (day == st->st_Day && month == st->st_Month && year == st->st_Year) ? st->st_Hour : 0; hour < 24; hour++)
  1825.                             if (cn->cn_Hour & (1L << hour))
  1826.                                 for (min = (hour == st->st_Hour && day == st->st_Day && month == st->st_Month && year == st->st_Year) ? st->st_Min + 1 : 0; min < 60; min++)
  1827.                                     if ((min > 31 && cn->cn_HiMin & (1L << (min - 32))) ||
  1828.                                         (min < 32 && cn->cn_LoMin & (1L << min)))
  1829.                                         return ((days2 + day) * SECSINDAY +
  1830.                                             (hour * SECSINHOUR) +
  1831.                                             (min * SECSINMINUTE));
  1832.             }
  1833.  
  1834.         days += isLeap ? 366 : 365;
  1835.     }
  1836.  
  1837.     /*
  1838.      * if we get down here then we couldn't find anytime within a seven
  1839.      * year span that satisfies the requirements to run this event. we
  1840.      * return zero in this case to let the caller know that no match was
  1841.      * found
  1842.      */
  1843.     return 0;
  1844. }
  1845.  
  1846.  /*
  1847.   * this will take an ASCII time string and convert it into a bitmap for
  1848.   * storage in a CyberNode
  1849.   */
  1850.  
  1851. void ParseBits(ULONG * bits, STRPTR tstr)
  1852. {
  1853.     register char *ptr;
  1854.     int start, end;
  1855.     int save;
  1856.  
  1857.     if (*tstr == '*') {
  1858.         bits[0] = bits[1] = 0xFFFFFFFF;
  1859.         return;
  1860.     }
  1861.     else
  1862.         bits[0] = bits[1] = 0;
  1863.  
  1864.     for (;;) {
  1865.         ptr = tstr;
  1866.         while (isdigit(*ptr))
  1867.             ptr++;
  1868.  
  1869.         save = *ptr, *ptr = NULL;
  1870.         end = start = atol(tstr);
  1871.  
  1872.         if (save == '-') {
  1873.             tstr = ++ptr;
  1874.  
  1875.             while (isdigit(*ptr))
  1876.                 ptr++;
  1877.  
  1878.             save = *ptr, *ptr = NULL;
  1879.             end = atol(tstr);
  1880.         }
  1881.  
  1882.         if (start >= 0 && end >= start)
  1883.             while (start <= end) {
  1884.                 if (start >= 64)
  1885.                     break;
  1886.  
  1887.                 if (start < 32)
  1888.                     bits[0] |= 1L << start;
  1889.                 else
  1890.                     bits[1] |= 1L << (start - 32);
  1891.  
  1892.                 start++;
  1893.             }
  1894.  
  1895.         if (!save)
  1896.             break;
  1897.         else
  1898.             tstr = ptr + 1;
  1899.     }
  1900. }
  1901.  
  1902.  /* convert a bit field back into an ASCII time string */
  1903.  
  1904. void UnParseBits(ULONG * bits, STRPTR ptr, int lowBit, int hiBit)
  1905. {
  1906.     STRPTR tptr;
  1907.     int curBit, startBit;
  1908.     BOOL isOn, lastOn;
  1909.  
  1910.     /* first check to see if everything is specified and return "*" if so */
  1911.     for (curBit = lowBit; curBit <= hiBit; curBit++)
  1912.         if ((curBit < 32 && !(bits[0] & 1L << curBit)) || (curBit > 31 && !(bits[1] & 1L << (curBit - 32))))
  1913.             break;
  1914.  
  1915.     if (curBit == hiBit + 1) {
  1916.         strcpy(ptr, "*");
  1917.         return;
  1918.     }
  1919.  
  1920.     /* it's not "*" so walk through and build things the hard way */
  1921.     tptr = ptr;
  1922.     *tptr = 0;
  1923.     lastOn = FALSE;
  1924.  
  1925.     for (curBit = lowBit; curBit < hiBit + 2; curBit++) {
  1926.         if ((curBit < 32 && (bits[0] & 1L << curBit)) || (curBit > 31 && (bits[1] & 1L << (curBit - 32))))
  1927.             isOn = TRUE;
  1928.         else
  1929.             isOn = FALSE;
  1930.  
  1931.         if (isOn & !lastOn) {
  1932.             sprintf(tptr, "%ld", curBit);
  1933.             startBit = curBit;
  1934.         }
  1935.  
  1936.         if (!isOn)
  1937.             if (lastOn && startBit != curBit - 1)
  1938.                 sprintf(tptr, "-%ld,", curBit - 1);
  1939.             else if (tptr > ptr && *(tptr - 1) != ',')
  1940.                 strcpy(tptr, ",");
  1941.  
  1942.         tptr += strlen(tptr);
  1943.         lastOn = isOn;
  1944.     }
  1945.  
  1946.     if (tptr == ptr)
  1947.         strcpy(ptr, "*");    /* Uh oh.  Somehow we have a field
  1948.                      * with nothing specified.  Fill it
  1949.                      * in with a "*" */
  1950.  
  1951.     else
  1952.         *--tptr = '\0';
  1953.  
  1954.     return;
  1955. }
  1956.  
  1957.  /* find a specific CyberNode by name */
  1958.  
  1959. struct CyberNode *FindEvent(STRPTR name)
  1960. {
  1961.     struct CyberNode *cn;
  1962.     struct CyberNode *eventAddr;
  1963.  
  1964.     if (!name || name[0] == '\0')
  1965.         return (struct CyberNode *) NULL;
  1966.  
  1967.     if (name[0] == '0' && tolower(name[1]) == 'x')
  1968.         stch_l(&name[2], (long *) &eventAddr);
  1969.     else
  1970.         eventAddr = 0;
  1971.  
  1972.     for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
  1973.          cn = (struct CyberNode *) cn->cn_Node.ln_Succ)
  1974.         if (cn == eventAddr || (cn->cn_Name && stricmp(name, cn->cn_Name) == 0))
  1975.             return cn;
  1976.  
  1977.     return (struct CyberNode *) NULL;
  1978. }
  1979.  
  1980.  /*
  1981.   * this routine will walk through the event list and free all the nodes in
  1982.   * it of a given type.  If called with TRUE it will free crontab entries,
  1983.   * otherwise it will free Rexx entries
  1984.   */
  1985.  
  1986. void FreeEvents(BOOL DoCronTabEntries)
  1987. {
  1988.     register struct CyberNode *cn;
  1989.     register struct CyberNode *tcn;
  1990.     UBYTE Flags;
  1991.  
  1992.     for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
  1993.          cn = (struct CyberNode *) cn->cn_Node.ln_Succ) {
  1994.         Flags = cn->cn_Flags & CNF_CRONTAB;
  1995.  
  1996.         if ((DoCronTabEntries && Flags) || (!DoCronTabEntries && !Flags)) {
  1997.             tcn = (struct CyberNode *) cn->cn_Node.ln_Pred;
  1998.             DeleteEvent(cn);
  1999.             cn = tcn;
  2000.         }
  2001.     }
  2002. }
  2003.  
  2004.  /*
  2005.   * this will delete the resources used for a specific event.  it checks to
  2006.   * see if there are any QueueFIFO messages pending for the specified event
  2007.   * and removes them as needed.
  2008.   */
  2009.  
  2010. void DeleteEvent(struct CyberNode * cn)
  2011. {
  2012.     register struct QueueFIFO *qf;
  2013.     register struct QueueFIFO *tqf;
  2014.  
  2015.     if (cn->cn_DelayedCount) {
  2016.         for (qf = (struct QueueFIFO *) & jobQueue[cn->cn_ObeyQueue].jq_FIFOList;
  2017.              qf->qf_Node.mln_Succ; qf = (struct QueueFIFO *) qf->qf_Node.mln_Succ) {
  2018.             if (qf->qf_CyberNode == cn) {
  2019.                 tqf = (struct QueueFIFO *) qf->qf_Node.mln_Pred;
  2020.                 Remove((struct Node *) qf);
  2021.                 FreeVec(qf);
  2022.                 qf = tqf;
  2023.             }
  2024.         }
  2025.     }
  2026.  
  2027.     Remove((struct Node *) cn);
  2028.     FreeVec(cn);
  2029. }
  2030.  
  2031.  /*
  2032.   * this allocates an output filehandle for us which output will be piped
  2033.   * through to sendmail over
  2034.   */
  2035.  
  2036. #define SSM_Buf BigBufOne
  2037. #define SSM_Buf2 BigBufTwo
  2038.  
  2039. BPTR SetupSendMail(STRPTR cmdName, STRPTR cmdArgs, STRPTR userName)
  2040. {
  2041.     BPTR pfho, pfhi, ofh;
  2042.     UBYTE pipeName[36];
  2043.  
  2044.     struct timeval tr_time;
  2045.  
  2046.     GetSysTime(&tr_time);
  2047.  
  2048.     sprintf(pipeName, "PIPE:CyberCron.%08lx.%08lx", tr_time.tv_secs, tr_time.tv_micro);
  2049.  
  2050.     sprintf(SSM_Buf2, GetString(&LocaleInfo, MSG_REALNAME_TEMPLATE), CYBERCRON, VersionID);
  2051.     sprintf(SSM_Buf, SendMailCmd, "cybercron", SSM_Buf2);
  2052.  
  2053.     ofh = Open("NIL:", MODE_NEWFILE);
  2054.     if (!ofh)
  2055.         return NULL;
  2056.  
  2057.     pfho = Open(pipeName, MODE_NEWFILE);
  2058.     if (!pfho) {
  2059.         Close(ofh);
  2060.         return NULL;
  2061.     }
  2062.  
  2063.     pfhi = Open(pipeName, MODE_OLDFILE);
  2064.     if (!pfhi) {
  2065.         Close(pfho);
  2066.         Close(ofh);
  2067.         return NULL;
  2068.     }
  2069.  
  2070.     if (SystemTags(SSM_Buf, SYS_Input, pfhi, SYS_Output, ofh, SYS_Asynch, TRUE,
  2071.                NP_StackSize, 32768, NP_CopyVars, TRUE, NP_Cli, TRUE, TAG_DONE) == -1) {
  2072.         Close(pfho);
  2073.         Close(pfhi);
  2074.         Close(ofh);
  2075.         return NULL;
  2076.     }
  2077.  
  2078.     FPrintf(pfho, GetString(&LocaleInfo, MSG_TO_SUBJECT), userName, cmdName, (cmdArgs ? " " : NULL), cmdArgs);
  2079.     Flush(pfho);
  2080.  
  2081.     return pfho;
  2082. }
  2083.  
  2084. UBYTE SJ_Buf[1024 + 12];
  2085. UBYTE CN_Buf[sizeof(SJ_Buf)];
  2086.  
  2087.  /* this routine will start up a job using System() */
  2088.  
  2089. void StartSystemJob(struct CyberNode * cn)
  2090. {
  2091.     BPTR ifh, ofh /* , efh */ ;
  2092.     struct SystemECdata *ecdata;
  2093.  
  2094.     struct TagItem tlist[20];
  2095.     int tlistIdx = 0;
  2096.  
  2097.     if (cn->cn_Args) {
  2098.         if (cn->cn_CommandHasSpace)
  2099.             sprintf(SJ_Buf, "\"%s\" %s", cn->cn_Command, cn->cn_Args);
  2100.         else
  2101.             sprintf(SJ_Buf, "%s %s", cn->cn_Command, cn->cn_Args);
  2102.  
  2103.         sprintf(CN_Buf, "\"»%s« %s\"", cn->cn_Command, cn->cn_Args);
  2104.     }
  2105.     else {
  2106.         if (cn->cn_CommandHasSpace)
  2107.             sprintf(SJ_Buf, "\"%s\"", cn->cn_Command);
  2108.         else
  2109.             strcpy(SJ_Buf, cn->cn_Command);
  2110.  
  2111.         sprintf(CN_Buf, "\"»%s«\"", cn->cn_Command);
  2112.     }
  2113.  
  2114.     if (!(ecdata = AllocVec(sizeof(struct SystemECdata), MEMF_CLEAR)))
  2115.         return;
  2116.  
  2117.     ecdata->jobNo = GetJobNum();
  2118.     if (!ecdata->jobNo) {
  2119.         Log(MSG_JOB_TABLE_FULL, CN_Buf);
  2120.         FreeVec(ecdata);
  2121.         return;
  2122.     }
  2123.  
  2124.     if (cn->cn_ReDirIn)
  2125.         ifh = Open(cn->cn_ReDirIn, MODE_OLDFILE);
  2126.     else
  2127.         ifh = Open("NIL:", MODE_OLDFILE);
  2128.  
  2129.     if (!ifh) {
  2130.         Log(MSG_COULDNT_OPEN_REDIRECTION, GetString(&LocaleInfo, MSG_REDIRIN), CN_Buf);
  2131.         FreeJobNum(ecdata->jobNo);
  2132.         FreeVec(ecdata);
  2133.         return;
  2134.     }
  2135.  
  2136.     tlist[tlistIdx].ti_Tag = SYS_Input;
  2137.     tlist[tlistIdx++].ti_Data = (ULONG) ifh;
  2138.  
  2139.     if (cn->cn_SendToUser && SendMailCmd[0])
  2140.         ofh = SetupSendMail(cn->cn_Command, cn->cn_Args, cn->cn_SendToUser);
  2141.     else {
  2142.         if (cn->cn_ReDirOut)
  2143.             ofh = Open(cn->cn_ReDirOut, (cn->cn_Flags & CNF_OUTAPP ? MODE_READWRITE : MODE_NEWFILE));
  2144.         else
  2145.             ofh = Open("NIL:", MODE_NEWFILE);
  2146.  
  2147.         if (ofh && (cn->cn_Flags & CNF_OUTAPP))
  2148.             Seek(ofh, 0, OFFSET_END);
  2149.     }
  2150.  
  2151.     if (!ofh) {
  2152.         Log(MSG_COULDNT_OPEN_REDIRECTION, GetString(&LocaleInfo, MSG_REDIROUT), CN_Buf);
  2153.         Close(ifh);
  2154.         FreeJobNum(ecdata->jobNo);
  2155.         FreeVec(ecdata);
  2156.         return;
  2157.     }
  2158.  
  2159.     tlist[tlistIdx].ti_Tag = SYS_Output;
  2160.     tlist[tlistIdx++].ti_Data = (ULONG) ofh;
  2161.  
  2162.     tlist[tlistIdx].ti_Tag = SYS_Asynch;
  2163.     tlist[tlistIdx++].ti_Data = TRUE;
  2164.  
  2165. /*
  2166.     Sigh..  Randell tells me that StdErr is pretty much unofficially "not for use"
  2167.     under 2.0.  This is here for the day that it can be used.  All that should need
  2168.     to be done is to pull out the comments.  Oh well.
  2169.  
  2170.     Do not uncomment this code.  NP_Error and NP_ErrorClose are __IGNORED__ by the
  2171.     system right now so if you specify stderr redirection, it will open the file and never
  2172.     close it.
  2173.  
  2174.     if (cn->cn_ReDirErr) {
  2175.         efh = Open(cn->cn_ReDirErr, (cn->cn_Flags & CNF_ERRAPP ? MODE_READWRITE : MODE_NEWFILE));
  2176.         if (!efh) {
  2177.             Log(MSG_COULDNT_OPEN_REDIRECTION, GetString(&LocaleInfo, MSG_REDIRERR), CN_Buf);
  2178.             Close(ofh);
  2179.             Close(ifh);
  2180.             FreeJobNum(ecdata->jobNo);
  2181.             FreeVec(ecdata);
  2182.             return;
  2183.         }
  2184.  
  2185.         if (cn->cn_Flags & CNF_ERRAPP)
  2186.             Seek(efh, 0, OFFSET_END);
  2187.  
  2188.         tlist[tlistIdx].ti_Tag = NP_Error;
  2189.         tlist[tlistIdx++].ti_Data = (ULONG)efh;
  2190.         tlist[tlistIdx].ti_Tag = NP_CloseError;
  2191.         tlist[tlistIdx++].ti_Data = TRUE;
  2192.     } else {
  2193.         tlist[tlistIdx].ti_Tag = NP_Error;
  2194.         tlist[tlistIdx++].ti_Data = (ULONG)StdErr;
  2195.         tlist[tlistIdx].ti_Tag = NP_CloseError;
  2196.         tlist[tlistIdx++].ti_Data = FALSE;
  2197.     }
  2198. */
  2199.     tlist[tlistIdx].ti_Tag = NP_StackSize;
  2200.     tlist[tlistIdx++].ti_Data = (ULONG) cn->cn_Stack;
  2201.  
  2202.     tlist[tlistIdx].ti_Tag = NP_Priority;
  2203.     tlist[tlistIdx++].ti_Data = (ULONG) cn->cn_Priority;
  2204.  
  2205.     if (cn->cn_CustomShell) {
  2206.         tlist[tlistIdx].ti_Tag = SYS_CustomShell;
  2207.         tlist[tlistIdx++].ti_Data = (ULONG) cn->cn_CustomShell;
  2208.     }
  2209.     else {
  2210.         tlist[tlistIdx].ti_Tag = SYS_UserShell;
  2211.         tlist[tlistIdx++].ti_Data = ((cn->cn_Flags & CNF_SYSSH) ? FALSE : TRUE);
  2212.     }
  2213.  
  2214.     tlist[tlistIdx].ti_Tag = NP_Cli;
  2215.     tlist[tlistIdx++].ti_Data = TRUE;
  2216.  
  2217.     tlist[tlistIdx].ti_Tag = NP_CopyVars;
  2218.     tlist[tlistIdx++].ti_Data = TRUE;
  2219.  
  2220.     tlist[tlistIdx].ti_Tag = NP_ExitCode;
  2221.     tlist[tlistIdx++].ti_Data = (ULONG) & EndSystemJob;
  2222.  
  2223.     tlist[tlistIdx].ti_Tag = NP_ExitData;
  2224.     tlist[tlistIdx++].ti_Data = (ULONG) ecdata;
  2225.  
  2226.     tlist[tlistIdx].ti_Tag = TAG_DONE;
  2227.     tlist[tlistIdx].ti_Data = 0;
  2228.  
  2229.     ecdata->queueNo = cn->cn_ObeyQueue;
  2230.     ecdata->flags = cn->cn_Flags & CNF_NOLOG;
  2231.  
  2232.     if (SystemTagList(SJ_Buf, tlist) == -1) {
  2233.         Log(MSG_COULDNT_START_SYSTEM_JOB, CN_Buf);
  2234. /*
  2235.         See above for why this is currently commented out.
  2236.  
  2237.         if (cn->cn_ReDirErr)
  2238.             Close(efh);
  2239. */
  2240.         Close(ofh);
  2241.         Close(ifh);
  2242.         FreeJobNum(ecdata->jobNo);
  2243.         FreeVec(ecdata);
  2244.         return;
  2245.     }
  2246.  
  2247.     if (!(cn->cn_Flags & CNF_NOLOG))
  2248.         Log(MSG_JOB_STARTED, ecdata->jobNo, CN_Buf);
  2249.  
  2250.     ObtainSemaphore(&jobSema->mps_Sema);
  2251.     NumSystemJobs++;
  2252.     jobQueue[cn->cn_ObeyQueue].jq_Cur++;
  2253.     ReleaseSemaphore(&jobSema->mps_Sema);
  2254. }
  2255.  
  2256. int __saveds __asm EndSystemJob(register __d0 int rc, register __d1 struct SystemECdata * data)
  2257. {
  2258.     if (!data->flags)
  2259.         Log(MSG_JOB_ENDED, data->jobNo);
  2260.  
  2261.     FreeJobNum(data->jobNo);
  2262.  
  2263.     ObtainSemaphore(&jobSema->mps_Sema);
  2264.     NumSystemJobs--;
  2265.     jobQueue[data->queueNo].jq_Cur--;
  2266.     ReleaseSemaphore(&jobSema->mps_Sema);
  2267.  
  2268.     FreeVec(data);
  2269.  
  2270.     return rc;
  2271. }
  2272.  
  2273. void StartRexxJob(struct CyberNode * cn)
  2274. {
  2275.     struct RexxMsg *msg;
  2276.     struct MsgPort *rexxServerPort;
  2277.     UWORD jobNo;
  2278.     BPTR ifh, ofh;
  2279.  
  2280.     if (cn->cn_Command[0] == '`') {
  2281.         if (cn->cn_Args)
  2282.             sprintf(SJ_Buf, "%s %s", &cn->cn_Command[1], cn->cn_Args);
  2283.         else
  2284.             strcpy(SJ_Buf, &cn->cn_Command[1]);
  2285.  
  2286.         sprintf(CN_Buf, "\"%s\"", SJ_Buf);
  2287.     }
  2288.     else {
  2289.         strcpy(SJ_Buf, cn->cn_Command);
  2290.         sprintf(CN_Buf, "\"»%s«%s%s\"", cn->cn_Command, (cn->cn_Args ? " " : NULL), cn->cn_Args);
  2291.     }
  2292.  
  2293.     if (GetARexxLib() == NULL) {
  2294.         ErrorMsg(MSG_CANT_START_AREXX_NO_LIB, CN_Buf, RXSNAME);
  2295.         Log(MSG_CANT_START_AREXX_NO_LIB, CN_Buf, RXSNAME);
  2296.         return;
  2297.     }
  2298.  
  2299.     jobNo = GetJobNum();
  2300.     if (!jobNo) {
  2301.         Log(MSG_JOB_TABLE_FULL, CN_Buf);
  2302.         FreeARexxLib();
  2303.         return;
  2304.     }
  2305.  
  2306.     if (cn->cn_ReDirIn)
  2307.         ifh = Open(cn->cn_ReDirIn, MODE_OLDFILE);
  2308.     else
  2309.         ifh = Open("NIL:", MODE_OLDFILE);
  2310.  
  2311.     if (!ifh) {
  2312.         Log(MSG_COULDNT_OPEN_REDIRECTION, GetString(&LocaleInfo, MSG_REDIRIN), CN_Buf);
  2313.         FreeJobNum(jobNo);
  2314.         FreeARexxLib();
  2315.         return;
  2316.     }
  2317.  
  2318.     if (cn->cn_SendToUser && SendMailCmd[0])
  2319.         ofh = SetupSendMail((cn->cn_Command[0] == '`' ? SJ_Buf : cn->cn_Command),
  2320.                     (cn->cn_Command[0] == '`' ? NULL : cn->cn_Args), cn->cn_SendToUser);
  2321.     else {
  2322.         if (cn->cn_ReDirOut)
  2323.             ofh = Open(cn->cn_ReDirOut, (cn->cn_Flags & CNF_OUTAPP ? MODE_READWRITE : MODE_NEWFILE));
  2324.         else
  2325.             ofh = Open("NIL:", MODE_NEWFILE);
  2326.  
  2327.         if (ofh && (cn->cn_Flags & CNF_OUTAPP))
  2328.             Seek(ofh, 0, OFFSET_END);
  2329.     }
  2330.  
  2331.     if (!ofh) {
  2332.         Log(MSG_COULDNT_OPEN_REDIRECTION, GetString(&LocaleInfo, MSG_REDIROUT), CN_Buf);
  2333.         Close(ifh);
  2334.         FreeJobNum(jobNo);
  2335.         FreeARexxLib();
  2336.         return;
  2337.     }
  2338.  
  2339.     if (!(msg = CreateRexxMsg(RexxPort, "rexx", RXSDIR))) {
  2340.         Log(MSG_CANT_CREATE_AREXX_OBJECT, GetString(&LocaleInfo, MSG_REXXMSG), CN_Buf);
  2341.         Close(ofh);
  2342.         Close(ifh);
  2343.         FreeJobNum(jobNo);
  2344.         FreeARexxLib();
  2345.         return;
  2346.     }
  2347.  
  2348.     if (!(msg->rm_Args[0] = CreateArgstring(SJ_Buf, strlen(SJ_Buf)))) {
  2349.         Log(MSG_CANT_CREATE_AREXX_OBJECT, GetString(&LocaleInfo, MSG_ARGSTRING), CN_Buf);
  2350.         DeleteRexxMsg(msg);
  2351.         Close(ofh);
  2352.         Close(ifh);
  2353.         FreeJobNum(jobNo);
  2354.         FreeARexxLib();
  2355.         return;
  2356.     }
  2357.  
  2358.     if (cn->cn_Args && cn->cn_Command[0] != '`') {
  2359.         if (!(msg->rm_Args[1] = CreateArgstring(cn->cn_Args, strlen(cn->cn_Args)))) {
  2360.             Log(MSG_CANT_CREATE_AREXX_OBJECT, GetString(&LocaleInfo, MSG_ARGSTRING), CN_Buf);
  2361.             DeleteArgstring(msg->rm_Args[0]);
  2362.             DeleteRexxMsg(msg);
  2363.             Close(ofh);
  2364.             Close(ifh);
  2365.             FreeJobNum(jobNo);
  2366.             FreeARexxLib();
  2367.             return;
  2368.         }
  2369.     }
  2370.     else
  2371.         msg->rm_Args[1] = NULL;
  2372.  
  2373.     msg->rm_Action = RXFUNC;
  2374.     if (cn->cn_Command[0] == '`')
  2375.         msg->rm_Action |= RXFF_STRING;
  2376.  
  2377.     if (msg->rm_Args[1])
  2378.         msg->rm_Action |= 1;    /* one argument string passed in */
  2379.  
  2380.     msg->rm_Args[2] = (STRPTR) jobNo;
  2381.     msg->rm_Args[3] = (STRPTR) (cn->cn_Flags & CNF_NOLOG);
  2382.     msg->rm_Args[4] = (STRPTR) cn->cn_ObeyQueue;
  2383.  
  2384.     msg->rm_Stdin = ifh;
  2385.     msg->rm_Stdout = ofh;
  2386.  
  2387.     Forbid();
  2388.     if (rexxServerPort = FindPort(RXSDIR))
  2389.         PutMsg(rexxServerPort, (struct Message *) msg);
  2390.     Permit();
  2391.  
  2392.     if (rexxServerPort) {
  2393.         if (!msg->rm_Args[3])
  2394.             Log(MSG_JOB_STARTED, jobNo, CN_Buf);
  2395.         ObtainSemaphore(&jobSema->mps_Sema);
  2396.         NumARexxJobs++;
  2397.         jobQueue[cn->cn_ObeyQueue].jq_Cur++;
  2398.         ReleaseSemaphore(&jobSema->mps_Sema);
  2399.     }
  2400.     else {
  2401.         Log(MSG_CANT_FIND_REXX_HOST, RXSDIR, CN_Buf);
  2402.         if (msg->rm_Args[1])
  2403.             DeleteArgstring(msg->rm_Args[1]);
  2404.         DeleteArgstring(msg->rm_Args[0]);
  2405.         DeleteRexxMsg(msg);
  2406.         Close(ofh);
  2407.         Close(ifh);
  2408.         FreeJobNum(jobNo);
  2409.     }
  2410.  
  2411.     FreeARexxLib();
  2412. }
  2413.  
  2414.  /*
  2415.   * this routine will attempt to get a job number for us. it returns the job
  2416.   * number or 0 if no jobs are free.
  2417.   */
  2418.  
  2419. UWORD GetJobNum(void)
  2420. {
  2421.     register UWORD job;
  2422.     register int index;
  2423.     register UBYTE mask;
  2424.  
  2425.     ObtainSemaphore(&jobSema->mps_Sema);
  2426.  
  2427.     for (job = 0; job < JOB_TABLE_SIZE * sizeof(UBYTE); job++) {
  2428.         index = job / sizeof(UBYTE);
  2429.         mask = 1L << (job - index * sizeof(UBYTE));
  2430.  
  2431.         if (jobSema->mps_SemaData[index] & mask)
  2432.             continue;
  2433.  
  2434.         jobSema->mps_SemaData[index] |= mask;
  2435.         ReleaseSemaphore(&jobSema->mps_Sema);
  2436.         return (UWORD) (job + 1);
  2437.     }
  2438.  
  2439.     ReleaseSemaphore(&jobSema->mps_Sema);
  2440.     return (UWORD) 0;
  2441. }
  2442.  
  2443.  /* this routine will free a job number previously allocated */
  2444.  
  2445. void FreeJobNum(UWORD job)
  2446. {
  2447.     register int index;
  2448.     register UBYTE mask;
  2449.  
  2450.     if (!job || job >= JOB_TABLE_SIZE * sizeof(UBYTE))
  2451.         return;
  2452.  
  2453.     job--;
  2454.  
  2455.     index = job / sizeof(UBYTE);
  2456.     mask = 1L << (job - index * sizeof(UBYTE));
  2457.  
  2458.     ObtainSemaphore(&jobSema->mps_Sema);
  2459.     jobSema->mps_SemaData[index] &= ~mask;
  2460.     ReleaseSemaphore(&jobSema->mps_Sema);
  2461. }
  2462.  
  2463. void __stdargs Log(ULONG fmtId,...)
  2464. {
  2465.     va_list args;
  2466.     BPTR loghandle;
  2467.     struct DateTime dat;
  2468.     UBYTE Date[LEN_DATSTRING + 1];
  2469.     UBYTE Time[LEN_DATSTRING + 1];
  2470.  
  2471.     DateStamp(&dat.dat_Stamp);
  2472.  
  2473.     dat.dat_Format = FORMAT_DOS;
  2474.     dat.dat_Flags = 0;
  2475.     dat.dat_StrDay = NULL;
  2476.     dat.dat_StrDate = Date;
  2477.     dat.dat_StrTime = Time;
  2478.  
  2479.     memset(Date, 0, LEN_DATSTRING + 1);
  2480.     memset(Time, 0, LEN_DATSTRING + 1);
  2481.     DateToStr(&dat);
  2482.  
  2483.     va_start(args, fmtId);
  2484.  
  2485.     ObtainSemaphore(&logSema->mps_Sema);
  2486.     if (LogFile[0]) {
  2487.         if (OwnDevUnitBase)
  2488.             if (LockDevUnit("LOG-UPDATE.LOCK", 0, CYBERCRON, 0L))
  2489.                 goto dieLog;
  2490.  
  2491.         if (loghandle = Open(LogFile, MODE_READWRITE)) {
  2492.             Seek(loghandle, 0, OFFSET_END);
  2493.  
  2494.             FPrintf(loghandle, "(%s %s) ", Date, Time);
  2495.             VFPrintf(loghandle, GetString(&LocaleInfo, fmtId), (LONG *) args);
  2496.             FPutC(loghandle, '\n');
  2497.             Close(loghandle);
  2498.         }
  2499.  
  2500.         if (OwnDevUnitBase)
  2501.             FreeDevUnit("LOG-UPDATE.LOCK", 0);
  2502.     }
  2503.       dieLog:
  2504.     ReleaseSemaphore(&logSema->mps_Sema);
  2505.  
  2506.     va_end(args);
  2507. }
  2508.  
  2509.  /*
  2510.   * this routine sets up a public semaphore.  If it already exists we just
  2511.   * bump its use count up by one.  Otherwise we have to allocate a new one.
  2512.   * semaDataSize is a number of bytes to allocate with the semaphore that can
  2513.   * be shared by those locking the semaphore.  Let's us have "global" data
  2514.   * between seperate processes.  These routines don't care what is put in
  2515.   * this data space.  It will be cleared to 0s when first allocated, though.
  2516.   */
  2517.  
  2518. struct MyPublicSema *InitMyPublicSemaphore(STRPTR semaName, ULONG semaDataSize)
  2519. {
  2520.     struct MyPublicSema *theSema;
  2521.     ULONG semaNameLen;
  2522.  
  2523.     if (!semaName)
  2524.         return (struct MyPublicSema *) NULL;
  2525.  
  2526.     /*
  2527.      * we want the semaphore's name to be a longword in length, at least
  2528.      * in space allocated for it
  2529.      */
  2530.     semaNameLen = ((strlen(semaName) + 3) / 4) * 4;
  2531.  
  2532.     Forbid();
  2533.     if (theSema = (struct MyPublicSema *) FindSemaphore(semaName))
  2534.         theSema->mps_UseCount++;    /* only safe way to diddle
  2535.                          * with this is under
  2536.                          * Forbid()... sigh */
  2537.     else {
  2538.  
  2539.         /*
  2540.          * we didn't find the semaphore around already so we have to
  2541.          * allocate and initialize it
  2542.          */
  2543.         if (!(theSema = AllocVec(sizeof(struct MyPublicSema) + semaNameLen + semaDataSize, MEMF_CLEAR | MEMF_PUBLIC))) {
  2544.             Permit();
  2545.             return (struct MyPublicSema *) NULL;
  2546.         }
  2547.  
  2548.         strcpy(theSema->mps_SemaName, semaName);
  2549.         theSema->mps_Sema.ss_Link.ln_Name = theSema->mps_SemaName;
  2550.         if (semaDataSize)
  2551.             theSema->mps_SemaData = &theSema->mps_SemaName[semaNameLen];
  2552.         theSema->mps_UseCount = 1;
  2553.  
  2554.         AddSemaphore(&theSema->mps_Sema);
  2555.     }
  2556.  
  2557.     Permit();
  2558.  
  2559.     return theSema;
  2560. }
  2561.  
  2562.  /*
  2563.   * this dealocates what we got from InitMyPublicSemaphore().  If there are
  2564.   * others still using it then just down the use count.  Otherwise remove the
  2565.   * semaphore from the public lists and down the use count.
  2566.   */
  2567.  
  2568. void FreeMyPublicSemaphore(struct MyPublicSema * mySema)
  2569. {
  2570.     if (mySema) {
  2571.         ObtainSemaphore(&mySema->mps_Sema);
  2572.         Forbid();
  2573.         ReleaseSemaphore(&mySema->mps_Sema);
  2574.         if (!--mySema->mps_UseCount) {
  2575.             RemSemaphore(&mySema->mps_Sema);
  2576.             FreeVec(mySema);
  2577.         }
  2578.         Permit();
  2579.     }
  2580. }
  2581.  
  2582.  /*
  2583.   * this function will open the ARexx library for us.  it handles nested
  2584.   * calls to open the library such that we only call OpenLibrary() once. each
  2585.   * time a rexx command is run, we call this routine to open the library and
  2586.   * when the RexxMsg comes back we call FreeARexxLib() to decrement the nest
  2587.   * count.
  2588.   */
  2589.  
  2590. BOOL GetARexxLib(void)
  2591. {
  2592.     if (ARexxLibCount) {
  2593.         ARexxLibCount++;
  2594.         return TRUE;
  2595.     }
  2596.  
  2597.     if (!(RexxSysBase = (struct RxsLib *) OpenLibrary(RXSNAME, 0)))
  2598.         return FALSE;
  2599.  
  2600.     ARexxLibCount = 1;
  2601.     return TRUE;
  2602. }
  2603.  
  2604.  /*
  2605.   * this routine is the opposite of GetARexxLib().  it frees a nested open
  2606.   * count for the ARexx library.  if the count goes to zero then we call
  2607.   * CloseLibrary() to free the library for real.
  2608.   */
  2609.  
  2610. void FreeARexxLib(void)
  2611. {
  2612.     if (!--ARexxLibCount)
  2613.         CloseLibrary((struct Library *) RexxSysBase);
  2614. }
  2615.  
  2616.  /*
  2617.   * we use this function to send error messages to StdErr.  We prefix all
  2618.   * messages with "CyberCron:" since the message might be dumped into a
  2619.   * stream with other people sending output to it
  2620.   */
  2621.  
  2622. void __stdargs ErrorMsg(ULONG fmtId,...)
  2623. {
  2624.     va_list args;
  2625.  
  2626.     struct IntuitionBase *IntuitionBase;
  2627.     ULONG my_IDCMP;
  2628.     struct EasyStruct ezRequest;
  2629.  
  2630.  
  2631.     if (ErrorsToStdErr) {
  2632.         if (StdErr) {
  2633.             FPrintf(StdErr, "%s:  ", CYBERCRON);
  2634.  
  2635.             va_start(args, fmtId);
  2636.             VFPrintf(StdErr, GetString(&LocaleInfo, fmtId), (LONG *) args);
  2637.             FPutC(StdErr, '\n');
  2638.             Flush(StdErr);
  2639.             va_end(args);
  2640.         }
  2641.     }
  2642.     else {
  2643.         /* grab intuition, do an EasyRequest and then free intuition */
  2644.         if (IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 37L)) {
  2645.             ezRequest.es_StructSize = sizeof(struct EasyStruct);
  2646.             ezRequest.es_Flags = 0;
  2647.             ezRequest.es_Title = GetString(&LocaleInfo, MSG_REQWINTITLE);
  2648.             ezRequest.es_TextFormat = GetString(&LocaleInfo, fmtId);
  2649.             ezRequest.es_GadgetFormat = GetString(&LocaleInfo, MSG_CONTINUE_GAD);
  2650.  
  2651.             my_IDCMP = 0L;
  2652.  
  2653.             va_start(args, fmtId);
  2654.             EasyRequestArgs((struct Window *) NULL, &ezRequest, &my_IDCMP, args);
  2655.             va_end(args);
  2656.  
  2657.             CloseLibrary((struct Library *) IntuitionBase);
  2658.         }
  2659.     }
  2660. }
  2661.  
  2662. void GetSystemTime(SystemTime_t * st, BOOL stIsSet)
  2663. {
  2664.     struct timeval tr_time;
  2665.     struct ClockData cd;
  2666.  
  2667.     if (!stIsSet) {
  2668.         GetSysTime(&tr_time);
  2669.         st->st_tvsecs = tr_time.tv_secs;
  2670.     }
  2671.  
  2672.     Amiga2Date(st->st_tvsecs, &cd);
  2673.  
  2674.     st->st_Year = cd.year;
  2675.     st->st_Month = cd.month;
  2676.     st->st_Day = cd.mday;
  2677.  
  2678.     st->st_Hour = cd.hour;
  2679.     st->st_Min = cd.min;
  2680.     st->st_Sec = cd.sec;
  2681.  
  2682.     st->st_DOW = cd.wday;
  2683. }
  2684.